diff options
Diffstat (limited to 'src/proguard/optimize/evaluation/PartialEvaluator.java')
-rw-r--r-- | src/proguard/optimize/evaluation/PartialEvaluator.java | 1282 |
1 files changed, 0 insertions, 1282 deletions
diff --git a/src/proguard/optimize/evaluation/PartialEvaluator.java b/src/proguard/optimize/evaluation/PartialEvaluator.java deleted file mode 100644 index 0301f12..0000000 --- a/src/proguard/optimize/evaluation/PartialEvaluator.java +++ /dev/null @@ -1,1282 +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.*; -import proguard.classfile.constant.ClassConstant; -import proguard.classfile.instruction.*; -import proguard.classfile.util.*; -import proguard.classfile.visitor.*; -import proguard.evaluation.*; -import proguard.evaluation.value.*; -import proguard.optimize.peephole.BranchTargetFinder; - -import java.util.Arrays; - -/** - * This AttributeVisitor performs partial evaluation on the code attributes - * that it visits. - * - * @author Eric Lafortune - */ -public class PartialEvaluator -extends SimplifiedVisitor -implements AttributeVisitor, - ExceptionInfoVisitor -{ - //* - private static final boolean DEBUG = false; - private static final boolean DEBUG_RESULTS = false; - /*/ - private static boolean DEBUG = true; - private static boolean DEBUG_RESULTS = true; - //*/ - - private static final int MAXIMUM_EVALUATION_COUNT = 5; - - public static final int NONE = -2; - public static final int AT_METHOD_ENTRY = -1; - public static final int AT_CATCH_ENTRY = -1; - - private final ValueFactory valueFactory; - private final InvocationUnit invocationUnit; - private final boolean evaluateAllCode; - - private InstructionOffsetValue[] branchOriginValues = new InstructionOffsetValue[ClassConstants.TYPICAL_CODE_LENGTH]; - private InstructionOffsetValue[] branchTargetValues = new InstructionOffsetValue[ClassConstants.TYPICAL_CODE_LENGTH]; - private TracedVariables[] variablesBefore = new TracedVariables[ClassConstants.TYPICAL_CODE_LENGTH]; - private TracedStack[] stacksBefore = new TracedStack[ClassConstants.TYPICAL_CODE_LENGTH]; - private TracedVariables[] variablesAfter = new TracedVariables[ClassConstants.TYPICAL_CODE_LENGTH]; - private TracedStack[] stacksAfter = new TracedStack[ClassConstants.TYPICAL_CODE_LENGTH]; - private boolean[] generalizedContexts = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; - private int[] evaluationCounts = new int[ClassConstants.TYPICAL_CODE_LENGTH]; - private boolean evaluateExceptions; - - private final BasicBranchUnit branchUnit; - private final BranchTargetFinder branchTargetFinder; - - private final java.util.Stack callingInstructionBlockStack; - private final java.util.Stack instructionBlockStack = new java.util.Stack(); - - - /** - * Creates a simple PartialEvaluator. - */ - public PartialEvaluator() - { - this(new ValueFactory(), - new BasicInvocationUnit(new ValueFactory()), - true); - } - - - /** - * Creates a new PartialEvaluator. - * @param valueFactory the value factory that will create all values - * during evaluation. - * @param invocationUnit the invocation unit that will handle all - * communication with other fields and methods. - * @param evaluateAllCode a flag that specifies whether all casts, branch - * targets, and exception handlers should be - * evaluated, even if they are unnecessary or - * unreachable. - */ - public PartialEvaluator(ValueFactory valueFactory, - InvocationUnit invocationUnit, - boolean evaluateAllCode) - { - this(valueFactory, - invocationUnit, - evaluateAllCode, - evaluateAllCode ? - new BasicBranchUnit() : - new TracedBranchUnit(), - new BranchTargetFinder(), - null); - } - - - /** - * Creates a new PartialEvaluator, based on an existing one. - * @param partialEvaluator the subroutine calling partial evaluator. - */ - private PartialEvaluator(PartialEvaluator partialEvaluator) - { - this(partialEvaluator.valueFactory, - partialEvaluator.invocationUnit, - partialEvaluator.evaluateAllCode, - partialEvaluator.branchUnit, - partialEvaluator.branchTargetFinder, - partialEvaluator.instructionBlockStack); - } - - - /** - * Creates a new PartialEvaluator. - * @param valueFactory the value factory that will create - * all values during evaluation. - * @param invocationUnit the invocation unit that will handle - * all communication with other fields - * and methods. - * @param evaluateAllCode a flag that specifies whether all - * casts, branch targets, and exception - * handlers should be evaluated, even - * if they are unnecessary or - * unreachable. - * @param branchUnit the branch unit that will handle all - * branches. - * @param branchTargetFinder the utility class that will find all - * branches. - * @param callingInstructionBlockStack the stack of instruction blocks to - * be evaluated - */ - private PartialEvaluator(ValueFactory valueFactory, - InvocationUnit invocationUnit, - boolean evaluateAllCode, - BasicBranchUnit branchUnit, - BranchTargetFinder branchTargetFinder, - java.util.Stack callingInstructionBlockStack) - { - this.valueFactory = valueFactory; - this.invocationUnit = invocationUnit; - this.evaluateAllCode = evaluateAllCode; - this.branchUnit = branchUnit; - this.branchTargetFinder = branchTargetFinder; - this.callingInstructionBlockStack = callingInstructionBlockStack == null ? - this.instructionBlockStack : - callingInstructionBlockStack; - } - - - // Implementations for AttributeVisitor. - - public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} - - - public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) - { -// DEBUG = DEBUG_RESULTS = -// clazz.getName().equals("abc/Def") && -// method.getName(clazz).equals("abc"); - - // TODO: Remove this when the partial evaluator 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 performing 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()+")"); - - if (DEBUG) - { - method.accept(clazz, new ClassPrinter()); - - System.out.println("Evaluation results:"); - - int offset = 0; - do - { - if (isBranchOrExceptionTarget(offset)) - { - System.out.println("Branch target from ["+branchOriginValues[offset]+"]:"); - if (isTraced(offset)) - { - System.out.println(" Vars: "+variablesBefore[offset]); - System.out.println(" Stack: "+stacksBefore[offset]); - } - } - - Instruction instruction = InstructionFactory.create(codeAttribute.code, - offset); - System.out.println(instruction.toString(offset)); - - if (isTraced(offset)) - { - int initializationOffset = branchTargetFinder.initializationOffset(offset); - if (initializationOffset != NONE) - { - System.out.println(" is to be initialized at ["+initializationOffset+"]"); - } - - InstructionOffsetValue branchTargets = branchTargets(offset); - if (branchTargets != null) - { - System.out.println(" has overall been branching to "+branchTargets); - } - - System.out.println(" Vars: "+variablesAfter[offset]); - System.out.println(" Stack: "+stacksAfter[offset]); - } - - offset += instruction.length(offset); - } - while (offset < codeAttribute.u4codeLength); - } - - throw ex; - } - } - - - public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute) - { - // Evaluate the instructions, starting at the entry point. - if (DEBUG) - { - System.out.println(); - System.out.println("Partial evaluation: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); - System.out.println(" Max locals = "+codeAttribute.u2maxLocals); - System.out.println(" Max stack = "+codeAttribute.u2maxStack); - } - - // Reuse the existing variables and stack objects, ensuring the right size. - TracedVariables variables = new TracedVariables(codeAttribute.u2maxLocals); - TracedStack stack = new TracedStack(codeAttribute.u2maxStack); - - // Initialize the reusable arrays and variables. - initializeArrays(codeAttribute); - initializeParameters(clazz, method, codeAttribute, variables); - - // Find all instruction offsets,... - codeAttribute.accept(clazz, method, branchTargetFinder); - - // Start executing the first instruction block. - evaluateInstructionBlockAndExceptionHandlers(clazz, - method, - codeAttribute, - variables, - stack, - 0, - codeAttribute.u4codeLength); - - if (DEBUG_RESULTS) - { - System.out.println("Evaluation results:"); - - int offset = 0; - do - { - if (isBranchOrExceptionTarget(offset)) - { - System.out.println("Branch target from ["+branchOriginValues[offset]+"]:"); - if (isTraced(offset)) - { - System.out.println(" Vars: "+variablesBefore[offset]); - System.out.println(" Stack: "+stacksBefore[offset]); - } - } - - Instruction instruction = InstructionFactory.create(codeAttribute.code, - offset); - System.out.println(instruction.toString(offset)); - - if (isTraced(offset)) - { - int initializationOffset = branchTargetFinder.initializationOffset(offset); - if (initializationOffset >= 0) - { - System.out.println(" is to be initialized at ["+initializationOffset+"]"); - } - - InstructionOffsetValue branchTargets = branchTargets(offset); - if (branchTargets != null) - { - System.out.println(" has overall been branching to "+branchTargets); - } - - System.out.println(" Vars: "+variablesAfter[offset]); - System.out.println(" Stack: "+stacksAfter[offset]); - } - - offset += instruction.length(offset); - } - while (offset < codeAttribute.u4codeLength); - } - } - - - /** - * Returns whether a block of instructions is ever used. - */ - public boolean isTraced(int startOffset, int endOffset) - { - for (int index = startOffset; index < endOffset; index++) - { - if (isTraced(index)) - { - return true; - } - } - - return false; - } - - - /** - * Returns whether the instruction at the given offset has ever been - * executed during the partial evaluation. - */ - public boolean isTraced(int instructionOffset) - { - return evaluationCounts[instructionOffset] > 0; - } - - - /** - * Returns whether there is an instruction at the given offset. - */ - public boolean isInstruction(int instructionOffset) - { - return branchTargetFinder.isInstruction(instructionOffset); - } - - - /** - * Returns whether the instruction at the given offset is the target of a - * branch instruction or an exception. - */ - public boolean isBranchOrExceptionTarget(int instructionOffset) - { - return branchTargetFinder.isBranchTarget(instructionOffset) || - branchTargetFinder.isExceptionHandler(instructionOffset); - } - - - /** - * Returns whether the instruction at the given offset is the start of a - * subroutine. - */ - public boolean isSubroutineStart(int instructionOffset) - { - return branchTargetFinder.isSubroutineStart(instructionOffset); - } - - - /** - * Returns whether the instruction at the given offset is a subroutine - * invocation. - */ - public boolean isSubroutineInvocation(int instructionOffset) - { - return branchTargetFinder.isSubroutineInvocation(instructionOffset); - } - - - /** - * Returns whether the instruction at the given offset is part of a - * subroutine. - */ - public boolean isSubroutine(int instructionOffset) - { - return branchTargetFinder.isSubroutine(instructionOffset); - } - - - /** - * Returns whether the subroutine at the given offset is ever returning - * by means of a regular 'ret' instruction. - */ - public boolean isSubroutineReturning(int instructionOffset) - { - return branchTargetFinder.isSubroutineReturning(instructionOffset); - } - - - /** - * Returns the offset after the subroutine that starts at the given - * offset. - */ - public int subroutineEnd(int instructionOffset) - { - return branchTargetFinder.subroutineEnd(instructionOffset); - } - - - /** - * Returns the instruction offset at which the object instance that is - * created at the given 'new' instruction offset is initialized, or - * <code>NONE</code> if it is not being created. - */ - public int initializationOffset(int instructionOffset) - { - return branchTargetFinder.initializationOffset(instructionOffset); - } - - - /** - * Returns whether the method is an instance initializer. - */ - public boolean isInitializer() - { - return branchTargetFinder.isInitializer(); - } - - - /** - * Returns the instruction offset at which this initializer is calling - * the "super" or "this" initializer method, or <code>NONE</code> if it is - * not an initializer. - */ - public int superInitializationOffset() - { - return branchTargetFinder.superInitializationOffset(); - } - - - /** - * Returns the offset of the 'new' instruction that corresponds to the - * invocation of the instance initializer at the given offset, or - * <code>AT_METHOD_ENTRY</code> if the invocation is calling the "super" or - * "this" initializer method, , or <code>NONE</code> if it is not a 'new' - * instruction. - */ - public int creationOffset(int offset) - { - return branchTargetFinder.creationOffset(offset); - } - - - /** - * Returns the variables before execution of the instruction at the given - * offset. - */ - public TracedVariables getVariablesBefore(int instructionOffset) - { - return variablesBefore[instructionOffset]; - } - - - /** - * Returns the variables after execution of the instruction at the given - * offset. - */ - public TracedVariables getVariablesAfter(int instructionOffset) - { - return variablesAfter[instructionOffset]; - } - - - /** - * Returns the stack before execution of the instruction at the given - * offset. - */ - public TracedStack getStackBefore(int instructionOffset) - { - return stacksBefore[instructionOffset]; - } - - - /** - * Returns the stack after execution of the instruction at the given - * offset. - */ - public TracedStack getStackAfter(int instructionOffset) - { - return stacksAfter[instructionOffset]; - } - - - /** - * Returns the instruction offsets that branch to the given instruction - * offset. - */ - public InstructionOffsetValue branchOrigins(int instructionOffset) - { - return branchOriginValues[instructionOffset]; - } - - - /** - * Returns the instruction offsets to which the given instruction offset - * branches. - */ - public InstructionOffsetValue branchTargets(int instructionOffset) - { - return branchTargetValues[instructionOffset]; - } - - - // Utility methods to evaluate instruction blocks. - - /** - * Pushes block of instructions to be executed in the calling partial - * evaluator. - */ - private void pushCallingInstructionBlock(TracedVariables variables, - TracedStack stack, - int startOffset) - { - callingInstructionBlockStack.push(new MyInstructionBlock(variables, - stack, - startOffset)); - } - - - /** - * Pushes block of instructions to be executed in this partial evaluator. - */ - private void pushInstructionBlock(TracedVariables variables, - TracedStack stack, - int startOffset) - { - instructionBlockStack.push(new MyInstructionBlock(variables, - stack, - startOffset)); - } - - - /** - * Evaluates the instruction block and the exception handlers covering the - * given instruction range in the given code. - */ - private void evaluateInstructionBlockAndExceptionHandlers(Clazz clazz, - Method method, - CodeAttribute codeAttribute, - TracedVariables variables, - TracedStack stack, - int startOffset, - int endOffset) - { - evaluateInstructionBlock(clazz, - method, - codeAttribute, - variables, - stack, - startOffset); - - evaluateExceptionHandlers(clazz, - method, - codeAttribute, - startOffset, - endOffset); - } - - - /** - * Evaluates a block of instructions, starting at the given offset and ending - * at a branch instruction, a return instruction, or a throw instruction. - */ - private void evaluateInstructionBlock(Clazz clazz, - Method method, - CodeAttribute codeAttribute, - TracedVariables variables, - TracedStack stack, - int startOffset) - { - // Execute the initial instruction block. - evaluateSingleInstructionBlock(clazz, - method, - codeAttribute, - variables, - stack, - startOffset); - - // Execute all resulting instruction blocks on the execution stack. - while (!instructionBlockStack.empty()) - { - if (DEBUG) System.out.println("Popping alternative branch out of "+instructionBlockStack.size()+" blocks"); - - MyInstructionBlock instructionBlock = - (MyInstructionBlock)instructionBlockStack.pop(); - - evaluateSingleInstructionBlock(clazz, - method, - codeAttribute, - instructionBlock.variables, - instructionBlock.stack, - instructionBlock.startOffset); - } - } - - - /** - * Evaluates a block of instructions, starting at the given offset and ending - * at a branch instruction, a return instruction, or a throw instruction. - * Instruction blocks that are to be evaluated as a result are pushed on - * the given stack. - */ - private void evaluateSingleInstructionBlock(Clazz clazz, - Method method, - CodeAttribute codeAttribute, - TracedVariables variables, - TracedStack stack, - int startOffset) - { - byte[] code = codeAttribute.code; - - if (DEBUG) - { - System.out.println("Instruction block starting at ["+startOffset+"] in "+ - ClassUtil.externalFullMethodDescription(clazz.getName(), - 0, - method.getName(clazz), - method.getDescriptor(clazz))); - System.out.println("Init vars: "+variables); - System.out.println("Init stack: "+stack); - } - - Processor processor = new Processor(variables, - stack, - valueFactory, - branchUnit, - invocationUnit, - evaluateAllCode); - - int instructionOffset = startOffset; - - int maxOffset = startOffset; - - // Evaluate the subsequent instructions. - while (true) - { - if (maxOffset < instructionOffset) - { - maxOffset = instructionOffset; - } - - // Maintain a generalized local variable frame and stack at this - // instruction offset, before execution. - int evaluationCount = evaluationCounts[instructionOffset]; - if (evaluationCount == 0) - { - // First time we're passing by this instruction. - if (variablesBefore[instructionOffset] == null) - { - // There's not even a context at this index yet. - variablesBefore[instructionOffset] = new TracedVariables(variables); - stacksBefore[instructionOffset] = new TracedStack(stack); - } - else - { - // Reuse the context objects at this index. - variablesBefore[instructionOffset].initialize(variables); - stacksBefore[instructionOffset].copy(stack); - } - - // We'll execute in the generalized context, because it is - // the same as the current context. - generalizedContexts[instructionOffset] = true; - } - else - { - // Merge in the current context. - boolean variablesChanged = variablesBefore[instructionOffset].generalize(variables, true); - boolean stackChanged = stacksBefore[instructionOffset].generalize(stack); - - //System.out.println("GVars: "+variablesBefore[instructionOffset]); - //System.out.println("GStack: "+stacksBefore[instructionOffset]); - - // Bail out if the current context is the same as last time. - if (!variablesChanged && - !stackChanged && - generalizedContexts[instructionOffset]) - { - if (DEBUG) System.out.println("Repeated variables, stack, and branch targets"); - - break; - } - - // See if this instruction has been evaluated an excessive number - // of times. - if (evaluationCount >= MAXIMUM_EVALUATION_COUNT) - { - if (DEBUG) System.out.println("Generalizing current context after "+evaluationCount+" evaluations"); - - // Continue, but generalize the current context. - // Note that the most recent variable values have to remain - // last in the generalizations, for the sake of the ret - // instruction. - variables.generalize(variablesBefore[instructionOffset], false); - stack.generalize(stacksBefore[instructionOffset]); - - // We'll execute in the generalized context. - generalizedContexts[instructionOffset] = true; - } - else - { - // We'll execute in the current context. - generalizedContexts[instructionOffset] = false; - } - } - - // We'll evaluate this instruction. - evaluationCounts[instructionOffset]++; - - // Remember this instruction's offset with any stored value. - Value storeValue = new InstructionOffsetValue(instructionOffset); - variables.setProducerValue(storeValue); - stack.setProducerValue(storeValue); - - // Reset the trace value. - InstructionOffsetValue traceValue = InstructionOffsetValue.EMPTY_VALUE; - - // Note that the instruction is only volatile. - Instruction instruction = InstructionFactory.create(code, instructionOffset); - - // By default, the next instruction will be the one after this - // instruction. - int nextInstructionOffset = instructionOffset + - instruction.length(instructionOffset); - InstructionOffsetValue nextInstructionOffsetValue = new InstructionOffsetValue(nextInstructionOffset); - branchUnit.resetCalled(); - branchUnit.setTraceBranchTargets(nextInstructionOffsetValue); - - if (DEBUG) - { - System.out.println(instruction.toString(instructionOffset)); - } - - try - { - // Process the instruction. The processor may modify the - // variables and the stack, and it may call the branch unit - // and the invocation unit. - instruction.accept(clazz, - method, - codeAttribute, - instructionOffset, - processor); - } - catch (RuntimeException ex) - { - System.err.println("Unexpected error while evaluating instruction:"); - System.err.println(" Class = ["+clazz.getName()+"]"); - System.err.println(" Method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]"); - System.err.println(" Instruction = "+instruction.toString(instructionOffset)); - System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); - - throw ex; - } - - // Collect the branch targets from the branch unit. - InstructionOffsetValue branchTargets = branchUnit.getTraceBranchTargets(); - int branchTargetCount = branchTargets.instructionOffsetCount(); - - // Stop tracing. - branchUnit.setTraceBranchTargets(traceValue); - - if (DEBUG) - { - if (branchUnit.wasCalled()) - { - System.out.println(" is branching to "+branchTargets); - } - if (branchTargetValues[instructionOffset] != null) - { - System.out.println(" has up till now been branching to "+branchTargetValues[instructionOffset]); - } - - System.out.println(" Vars: "+variables); - System.out.println(" Stack: "+stack); - } - - // Maintain a generalized local variable frame and stack at this - // instruction offset, after execution. - if (evaluationCount == 0) - { - // First time we're passing by this instruction. - if (variablesAfter[instructionOffset] == null) - { - // There's not even a context at this index yet. - variablesAfter[instructionOffset] = new TracedVariables(variables); - stacksAfter[instructionOffset] = new TracedStack(stack); - } - else - { - // Reuse the context objects at this index. - variablesAfter[instructionOffset].initialize(variables); - stacksAfter[instructionOffset].copy(stack); - } - } - else - { - // Merge in the current context. - variablesAfter[instructionOffset].generalize(variables, true); - stacksAfter[instructionOffset].generalize(stack); - } - - // Did the branch unit get called? - if (branchUnit.wasCalled()) - { - // Accumulate the branch targets at this offset. - branchTargetValues[instructionOffset] = branchTargetValues[instructionOffset] == null ? - branchTargets : - branchTargetValues[instructionOffset].generalize(branchTargets).instructionOffsetValue(); - - // Are there no branch targets at all? - if (branchTargetCount == 0) - { - // Exit from this code block. - break; - } - - // Accumulate the branch origins at the branch target offsets. - InstructionOffsetValue instructionOffsetValue = new InstructionOffsetValue(instructionOffset); - for (int index = 0; index < branchTargetCount; index++) - { - int branchTarget = branchTargets.instructionOffset(index); - branchOriginValues[branchTarget] = branchOriginValues[branchTarget] == null ? - instructionOffsetValue: - branchOriginValues[branchTarget].generalize(instructionOffsetValue).instructionOffsetValue(); - } - - // Are there multiple branch targets? - if (branchTargetCount > 1) - { - // Push them on the execution stack and exit from this block. - for (int index = 0; index < branchTargetCount; index++) - { - if (DEBUG) System.out.println("Pushing alternative branch #"+index+" out of "+branchTargetCount+", from ["+instructionOffset+"] to ["+branchTargets.instructionOffset(index)+"]"); - - pushInstructionBlock(new TracedVariables(variables), - new TracedStack(stack), - branchTargets.instructionOffset(index)); - } - - break; - } - - if (DEBUG) System.out.println("Definite branch from ["+instructionOffset+"] to ["+branchTargets.instructionOffset(0)+"]"); - } - - // Just continue with the next instruction. - instructionOffset = branchTargets.instructionOffset(0); - - // Is this a subroutine invocation? - if (instruction.opcode == InstructionConstants.OP_JSR || - instruction.opcode == InstructionConstants.OP_JSR_W) - { - // Evaluate the subroutine in another partial evaluator. - evaluateSubroutine(clazz, - method, - codeAttribute, - variables, - stack, - instructionOffset, - instructionBlockStack); - - break; - } - else if (instruction.opcode == InstructionConstants.OP_RET) - { - // Let the partial evaluator that has called the subroutine - // handle the evaluation after the return. - pushCallingInstructionBlock(new TracedVariables(variables), - new TracedStack(stack), - instructionOffset); - break; - } - } - - if (DEBUG) System.out.println("Ending processing of instruction block starting at ["+startOffset+"]"); - } - - - /** - * Evaluates a subroutine and its exception handlers, starting at the given - * offset and ending at a subroutine return instruction. - */ - private void evaluateSubroutine(Clazz clazz, - Method method, - CodeAttribute codeAttribute, - TracedVariables variables, - TracedStack stack, - int subroutineStart, - java.util.Stack instructionBlockStack) - { - int subroutineEnd = branchTargetFinder.subroutineEnd(subroutineStart); - - if (DEBUG) System.out.println("Evaluating subroutine from "+subroutineStart+" to "+subroutineEnd); - - // Create a temporary partial evaluator, so there are no conflicts - // with variables that are alive across subroutine invocations, between - // different invocations. - PartialEvaluator subroutinePartialEvaluator = - new PartialEvaluator(this); - - subroutinePartialEvaluator.initializeArrays(codeAttribute); - - // Evaluate the subroutine. - subroutinePartialEvaluator.evaluateInstructionBlockAndExceptionHandlers(clazz, - method, - codeAttribute, - variables, - stack, - subroutineStart, - subroutineEnd); - - // Merge back the temporary partial evaluator. This way, we'll get - // the lowest common denominator of stacks and variables. - generalize(subroutinePartialEvaluator, 0, codeAttribute.u4codeLength); - - if (DEBUG) System.out.println("Ending subroutine from "+subroutineStart+" to "+subroutineEnd); - } - - - /** - * Generalizes the results of this partial evaluator with those of another - * given partial evaluator, over a given range of instructions. - */ - private void generalize(PartialEvaluator other, - int codeStart, - int codeEnd) - { - if (DEBUG) System.out.println("Generalizing with temporary partial evaluation"); - - for (int offset = codeStart; offset < codeEnd; offset++) - { - if (other.branchOriginValues[offset] != null) - { - branchOriginValues[offset] = branchOriginValues[offset] == null ? - other.branchOriginValues[offset] : - branchOriginValues[offset].generalize(other.branchOriginValues[offset]).instructionOffsetValue(); - } - - if (other.isTraced(offset)) - { - if (other.branchTargetValues[offset] != null) - { - branchTargetValues[offset] = branchTargetValues[offset] == null ? - other.branchTargetValues[offset] : - branchTargetValues[offset].generalize(other.branchTargetValues[offset]).instructionOffsetValue(); - } - - if (evaluationCounts[offset] == 0) - { - variablesBefore[offset] = other.variablesBefore[offset]; - stacksBefore[offset] = other.stacksBefore[offset]; - variablesAfter[offset] = other.variablesAfter[offset]; - stacksAfter[offset] = other.stacksAfter[offset]; - generalizedContexts[offset] = other.generalizedContexts[offset]; - evaluationCounts[offset] = other.evaluationCounts[offset]; - } - else - { - variablesBefore[offset].generalize(other.variablesBefore[offset], false); - stacksBefore[offset] .generalize(other.stacksBefore[offset]); - variablesAfter[offset] .generalize(other.variablesAfter[offset], false); - stacksAfter[offset] .generalize(other.stacksAfter[offset]); - //generalizedContexts[offset] - evaluationCounts[offset] += other.evaluationCounts[offset]; - } - } - } - } - - - /** - * Evaluates the exception handlers covering and targeting the given - * instruction range in the given code. - */ - private void evaluateExceptionHandlers(Clazz clazz, - Method method, - CodeAttribute codeAttribute, - int startOffset, - int endOffset) - { - if (DEBUG) System.out.println("Evaluating exceptions covering ["+startOffset+" -> "+endOffset+"]:"); - - ExceptionHandlerFilter exceptionEvaluator = - new ExceptionHandlerFilter(startOffset, - endOffset, - this); - - // Evaluate the exception catch blocks, until their entry variables - // have stabilized. - do - { - // Reset the flag to stop evaluating. - evaluateExceptions = false; - - // Evaluate all relevant exception catch blocks once. - codeAttribute.exceptionsAccept(clazz, - method, - startOffset, - endOffset, - exceptionEvaluator); - } - while (evaluateExceptions); - } - - - // Implementations for ExceptionInfoVisitor. - - public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) - { - int startPC = exceptionInfo.u2startPC; - int endPC = exceptionInfo.u2endPC; - - // Do we have to evaluate this exception catch block? - if (isTraced(startPC, endPC)) - { - int handlerPC = exceptionInfo.u2handlerPC; - int catchType = exceptionInfo.u2catchType; - - if (DEBUG) System.out.println("Evaluating exception ["+startPC +" -> "+endPC +": "+handlerPC+"]:"); - - // Reuse the existing variables and stack objects, ensuring the - // right size. - TracedVariables variables = new TracedVariables(codeAttribute.u2maxLocals); - TracedStack stack = new TracedStack(codeAttribute.u2maxStack); - - // Initialize the trace values. - Value storeValue = new InstructionOffsetValue(AT_CATCH_ENTRY); - variables.setProducerValue(storeValue); - stack.setProducerValue(storeValue); - - // Initialize the variables by generalizing the variables of the - // try block. Make sure to include the results of the last - // instruction for preverification. - generalizeVariables(startPC, - endPC, - evaluateAllCode, - variables); - - // Initialize the the stack. - //stack.push(valueFactory.createReference((ClassConstant)((ProgramClass)clazz).getConstant(exceptionInfo.u2catchType), false)); - String catchClassName = catchType != 0 ? - clazz.getClassName(catchType) : - ClassConstants.NAME_JAVA_LANG_THROWABLE; - - Clazz catchClass = catchType != 0 ? - ((ClassConstant)((ProgramClass)clazz).getConstant(catchType)).referencedClass : - null; - - stack.push(valueFactory.createReferenceValue(catchClassName, - catchClass, - false)); - - int evaluationCount = evaluationCounts[handlerPC]; - - // Evaluate the instructions, starting at the entry point. - evaluateInstructionBlock(clazz, - method, - codeAttribute, - variables, - stack, - handlerPC); - - // Remember to evaluate all exception handlers once more. - if (!evaluateExceptions) - { - evaluateExceptions = evaluationCount < evaluationCounts[handlerPC]; - } - } -// else if (evaluateAllCode) -// { -// if (DEBUG) System.out.println("No information for partial evaluation of exception ["+startPC +" -> "+endPC +": "+exceptionInfo.u2handlerPC+"] yet"); -// -// // We don't have any information on the try block yet, but we do -// // have to evaluate the exception handler. -// // Remember to evaluate all exception handlers once more. -// evaluateExceptions = true; -// } - else - { - if (DEBUG) System.out.println("No information for partial evaluation of exception ["+startPC +" -> "+endPC +": "+exceptionInfo.u2handlerPC+"]"); - } - } - - - // Small utility methods. - - /** - * Initializes the data structures for the variables, stack, etc. - */ - private void initializeArrays(CodeAttribute codeAttribute) - { - int codeLength = codeAttribute.u4codeLength; - - // Create new arrays for storing information at each instruction offset. - if (variablesAfter.length < codeLength) - { - // Create new arrays. - branchOriginValues = new InstructionOffsetValue[codeLength]; - branchTargetValues = new InstructionOffsetValue[codeLength]; - variablesBefore = new TracedVariables[codeLength]; - stacksBefore = new TracedStack[codeLength]; - variablesAfter = new TracedVariables[codeLength]; - stacksAfter = new TracedStack[codeLength]; - generalizedContexts = new boolean[codeLength]; - evaluationCounts = new int[codeLength]; - } - else - { - // Reset the arrays. - Arrays.fill(branchOriginValues, null); - Arrays.fill(branchTargetValues, null); - Arrays.fill(generalizedContexts, false); - Arrays.fill(evaluationCounts, 0); - - for (int index = 0; index < codeLength; index++) - { - if (variablesBefore[index] != null) - { - variablesBefore[index].reset(codeAttribute.u2maxLocals); - } - - if (stacksBefore[index] != null) - { - stacksBefore[index].reset(codeAttribute.u2maxStack); - } - - if (variablesAfter[index] != null) - { - variablesAfter[index].reset(codeAttribute.u2maxLocals); - } - - if (stacksAfter[index] != null) - { - stacksAfter[index].reset(codeAttribute.u2maxStack); - } - } - } - } - - - /** - * Initializes the data structures for the variables, stack, etc. - */ - private void initializeParameters(Clazz clazz, - Method method, - CodeAttribute codeAttribute, - TracedVariables variables) - { - // Create the method parameters. - TracedVariables parameters = new TracedVariables(codeAttribute.u2maxLocals); - - // Remember this instruction's offset with any stored value. - Value storeValue = new InstructionOffsetValue(AT_METHOD_ENTRY); - parameters.setProducerValue(storeValue); - - // Initialize the method parameters. - invocationUnit.enterMethod(clazz, method, parameters); - - if (DEBUG) - { - System.out.println(" Params: "+parameters); - } - - // Initialize the variables with the parameters. - variables.initialize(parameters); - - // Set the store value of each parameter variable. - InstructionOffsetValue atMethodEntry = new InstructionOffsetValue(AT_METHOD_ENTRY); - - for (int index = 0; index < parameters.size(); index++) - { - variables.setProducerValue(index, atMethodEntry); - } - } - - - /** - * Generalize the local variable frames of a block of instructions. - */ - private void generalizeVariables(int startOffset, - int endOffset, - boolean includeAfterLastInstruction, - TracedVariables generalizedVariables) - { - boolean first = true; - int lastIndex = -1; - - // Generalize the variables before each of the instructions in the block. - for (int index = startOffset; index < endOffset; index++) - { - if (isTraced(index)) - { - TracedVariables tracedVariables = variablesBefore[index]; - - if (first) - { - // Initialize the variables with the first traced local - // variable frame. - generalizedVariables.initialize(tracedVariables); - - first = false; - } - else - { - // Generalize the variables with the traced local variable - // frame. We can't use the return value, because local - // generalization can be different a couple of times, - // with the global generalization being the same. - generalizedVariables.generalize(tracedVariables, false); - } - - lastIndex = index; - } - } - - // Generalize the variables after the last instruction in the block, - // if required. - if (includeAfterLastInstruction && - lastIndex >= 0) - { - TracedVariables tracedVariables = variablesAfter[lastIndex]; - - if (first) - { - // Initialize the variables with the local variable frame. - generalizedVariables.initialize(tracedVariables); - } - else - { - // Generalize the variables with the local variable frame. - generalizedVariables.generalize(tracedVariables, false); - } - } - - // Just clear the variables if there aren't any traced instructions - // in the block. - if (first) - { - generalizedVariables.reset(generalizedVariables.size()); - } - } - - - private static class MyInstructionBlock - { - private TracedVariables variables; - private TracedStack stack; - private int startOffset; - - - private MyInstructionBlock(TracedVariables variables, - TracedStack stack, - int startOffset) - { - this.variables = variables; - this.stack = stack; - this.startOffset = startOffset; - } - } -} |