aboutsummaryrefslogtreecommitdiffstats
path: root/src/proguard/optimize/evaluation
diff options
context:
space:
mode:
Diffstat (limited to 'src/proguard/optimize/evaluation')
-rw-r--r--src/proguard/optimize/evaluation/EvaluationShrinker.java290
-rw-r--r--src/proguard/optimize/evaluation/EvaluationSimplifier.java15
-rw-r--r--src/proguard/optimize/evaluation/LivenessAnalyzer.java12
-rw-r--r--src/proguard/optimize/evaluation/LoadingInvocationUnit.java20
-rw-r--r--src/proguard/optimize/evaluation/PartialEvaluator.java141
-rw-r--r--src/proguard/optimize/evaluation/StoringInvocationUnit.java2
-rw-r--r--src/proguard/optimize/evaluation/TracedBranchUnit.java2
-rw-r--r--src/proguard/optimize/evaluation/VariableOptimizer.java127
8 files changed, 450 insertions, 159 deletions
diff --git a/src/proguard/optimize/evaluation/EvaluationShrinker.java b/src/proguard/optimize/evaluation/EvaluationShrinker.java
index 1463feb..bc5f658 100644
--- a/src/proguard/optimize/evaluation/EvaluationShrinker.java
+++ b/src/proguard/optimize/evaluation/EvaluationShrinker.java
@@ -2,7 +2,7 @@
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
- * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu)
+ * Copyright (c) 2002-2011 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
@@ -34,6 +34,8 @@ import proguard.evaluation.*;
import proguard.evaluation.value.*;
import proguard.optimize.info.*;
+import java.util.Arrays;
+
/**
* This AttributeVisitor simplifies the code attributes that it visits, based
* on partial evaluation.
@@ -55,13 +57,14 @@ implements AttributeVisitor
private final InstructionVisitor extraDeletedInstructionVisitor;
private final InstructionVisitor extraAddedInstructionVisitor;
- private final PartialEvaluator partialEvaluator;
- private final PartialEvaluator simplePartialEvaluator = new PartialEvaluator();
- private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true);
- private final MyUnusedParameterSimplifier unusedParameterSimplifier = new MyUnusedParameterSimplifier();
- private final MyProducerMarker producerMarker = new MyProducerMarker();
- private final MyStackConsistencyFixer stackConsistencyFixer = new MyStackConsistencyFixer();
- private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(false);
+ private final PartialEvaluator partialEvaluator;
+ private final PartialEvaluator simplePartialEvaluator = new PartialEvaluator();
+ private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true);
+ private final MyUnusedParameterSimplifier unusedParameterSimplifier = new MyUnusedParameterSimplifier();
+ private final MyProducerMarker producerMarker = new MyProducerMarker();
+ private final MyVariableInitializationMarker variableInitializationMarker = new MyVariableInitializationMarker();
+ private final MyStackConsistencyFixer stackConsistencyFixer = new MyStackConsistencyFixer();
+ private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(false);
private boolean[][] variablesNecessaryAfter = new boolean[ClassConstants.TYPICAL_CODE_LENGTH][ClassConstants.TYPICAL_VARIABLES_SIZE];
private boolean[][] stacksNecessaryAfter = new boolean[ClassConstants.TYPICAL_CODE_LENGTH][ClassConstants.TYPICAL_STACK_SIZE];
@@ -276,18 +279,10 @@ implements AttributeVisitor
if (partialEvaluator.isTraced(offset) &&
!isInstructionNecessary(offset))
{
- // Is the corresponding variable necessary anywhere in the code,
- // accoriding to a simple partial evaluation?
- int variableIndex = partialEvaluator.initializedVariable(offset);
- if (variableIndex >= 0 &&
- isVariableInitializationNecessary(clazz,
- method,
- codeAttribute,
- offset,
- variableIndex))
- {
- markInstruction(offset);
- }
+ Instruction instruction = InstructionFactory.create(codeAttribute.code,
+ offset);
+
+ instruction.accept(clazz, method, codeAttribute, offset, variableInitializationMarker);
}
}
if (DEBUG) System.out.println();
@@ -467,7 +462,9 @@ implements AttributeVisitor
*/
private class MyUnusedParameterSimplifier
extends SimplifiedVisitor
- implements InstructionVisitor, ConstantVisitor, MemberVisitor
+ implements InstructionVisitor,
+ ConstantVisitor,
+ MemberVisitor
{
private int invocationOffset;
private ConstantInstruction invocationInstruction;
@@ -657,6 +654,40 @@ implements AttributeVisitor
/**
+ * This InstructionVisitor marks variable initializations that are
+ * necessary to appease the JVM.
+ */
+ private class MyVariableInitializationMarker
+ extends SimplifiedVisitor
+ implements InstructionVisitor
+ {
+ // Implementations for InstructionVisitor.
+
+ public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
+
+
+ public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
+ {
+ if (!variableInstruction.isLoad())
+ {
+ int variableIndex = variableInstruction.variableIndex;
+
+ if (isVariableInitialization(offset,
+ variableIndex) &&
+ isVariableInitializationNecessary(clazz,
+ method,
+ codeAttribute,
+ offset,
+ variableIndex))
+ {
+ markInstruction(offset);
+ }
+ }
+ }
+ }
+
+
+ /**
* This InstructionVisitor fixes instructions locally, popping any unused
* produced stack entries after marked instructions, and popping produced
* stack entries and pushing missing stack entries instead of unmarked
@@ -687,12 +718,23 @@ implements AttributeVisitor
int requiredPushCount = 0;
for (int stackIndex = 0; stackIndex < popCount; stackIndex++)
{
- // Is the stack entry required by other consumers?
- if (!isStackSimplifiedBefore(offset, top - stackIndex) &&
- !isAnyStackEntryNecessaryAfter(tracedStack.getTopProducerValue(stackIndex).instructionOffsetValue(), top - stackIndex))
+ InstructionOffsetValue producerOffsets =
+ tracedStack.getTopProducerValue(stackIndex).instructionOffsetValue();
+
+ if (!isStackSimplifiedBefore(offset, top - stackIndex))
{
- // Remember to push it.
- requiredPushCount++;
+ // Is the stack entry pushed by any producer,
+ // because it is required by other consumers?
+ if (isAnyStackEntryNecessaryAfter(producerOffsets, top - stackIndex))
+ {
+ // Make sure it is pushed after all producers.
+ markStackEntriesAfter(producerOffsets, top - stackIndex);
+ }
+ else
+ {
+ // Remember to push it.
+ requiredPushCount++;
+ }
}
}
@@ -757,9 +799,16 @@ implements AttributeVisitor
int expectedPopCount = 0;
for (int stackIndex = 0; stackIndex < popCount; stackIndex++)
{
- // Is the stack entry required by other consumers?
- if (isAnyStackEntryNecessaryAfter(tracedStack.getTopProducerValue(stackIndex).instructionOffsetValue(), top - stackIndex))
+ InstructionOffsetValue producerOffsets =
+ tracedStack.getTopProducerValue(stackIndex).instructionOffsetValue();
+
+ // Is the stack entry pushed by any producer,
+ // because it is required by other consumers?
+ if (isAnyStackEntryNecessaryAfter(producerOffsets, top - stackIndex))
{
+ // Make sure it is pushed after all producers.
+ markStackEntriesAfter(producerOffsets, top - stackIndex);
+
// Remember to pop it.
expectedPopCount++;
}
@@ -1052,6 +1101,7 @@ implements AttributeVisitor
byte oldOpcode = instruction.opcode;
byte newOpcode = 0;
+ byte addOpcode = 0;
// Simplify the popping instruction if possible.
switch (oldOpcode)
@@ -1232,9 +1282,20 @@ implements AttributeVisitor
stackEntryPresent1)
{
// Will both elements be present?
- if (stackEntryPresent0 &&
- stackEntryPresent1)
+ if (!stackEntryPresent1)
{
+ // Pop the original top element (later bottom element).
+ newOpcode = InstructionConstants.OP_POP;
+ }
+ else if (!stackEntryPresent0)
+ {
+ // Swap both elements and pop the top one.
+ newOpcode = InstructionConstants.OP_SWAP;
+ addOpcode = InstructionConstants.OP_POP;
+ }
+ else
+ {
+ // Just swap both elements.
newOpcode = InstructionConstants.OP_SWAP;
}
}
@@ -1242,6 +1303,7 @@ implements AttributeVisitor
}
}
+ // Is there a replacement opcode?
if (newOpcode == 0)
{
// Delete the instruction.
@@ -1270,6 +1332,21 @@ implements AttributeVisitor
if (DEBUG) System.out.println(" Replacing instruction "+instruction.toString(dupOffset)+" by "+replacementInstruction.toString());
}
+
+ // Is there an additional opcode?
+ if (addOpcode != 0)
+ {
+ // Add the instruction.
+ Instruction additionalInstruction = new SimpleInstruction(addOpcode);
+ codeAttributeEditor.insertAfterInstruction(dupOffset, additionalInstruction);
+
+ if (extraAddedInstructionVisitor != null)
+ {
+ extraAddedInstructionVisitor.visitSimpleInstruction(null, null, null, dupOffset, null);
+ }
+
+ if (DEBUG) System.out.println(" Adding instruction "+additionalInstruction.toString(dupOffset));
+ }
}
@@ -1596,10 +1673,7 @@ implements AttributeVisitor
{
for (int offset = 0; offset < codeLength; offset++)
{
- for (int index = 0; index < maxLocals; index++)
- {
- variablesNecessaryAfter[offset][index] = false;
- }
+ Arrays.fill(variablesNecessaryAfter[offset], 0, maxLocals, false);
}
}
@@ -1612,10 +1686,7 @@ implements AttributeVisitor
{
for (int offset = 0; offset < codeLength; offset++)
{
- for (int index = 0; index < maxStack; index++)
- {
- stacksNecessaryAfter[offset][index] = false;
- }
+ Arrays.fill(stacksNecessaryAfter[offset], 0, maxStack, false);
}
}
@@ -1628,10 +1699,7 @@ implements AttributeVisitor
{
for (int offset = 0; offset < codeLength; offset++)
{
- for (int index = 0; index < maxStack; index++)
- {
- stacksSimplifiedBefore[offset][index] = false;
- }
+ Arrays.fill(stacksSimplifiedBefore[offset], 0, maxStack, false);
}
}
@@ -1641,10 +1709,7 @@ implements AttributeVisitor
}
else
{
- for (int index = 0; index < codeLength; index++)
- {
- instructionsNecessary[index] = false;
- }
+ Arrays.fill(instructionsNecessary, 0, codeLength, false);
}
}
@@ -1670,6 +1735,34 @@ implements AttributeVisitor
/**
+ * Returns whether the specified variable is initialized at the specified
+ * offset.
+ */
+ private boolean isVariableInitialization(int instructionOffset,
+ int variableIndex)
+ {
+ // Wasn't the variable set yet?
+ Value valueBefore = partialEvaluator.getVariablesBefore(instructionOffset).getValue(variableIndex);
+ if (valueBefore == null)
+ {
+ return true;
+ }
+
+ // Is the computational type different now?
+ Value valueAfter = partialEvaluator.getVariablesAfter(instructionOffset).getValue(variableIndex);
+ if (valueAfter.computationalType() != valueBefore.computationalType())
+ {
+ return true;
+ }
+
+ // Was the producer an argument (which may be removed)?
+ Value producersBefore = partialEvaluator.getVariablesBefore(instructionOffset).getProducerValue(variableIndex);
+ return producersBefore.instructionOffsetValue().instructionOffsetCount() == 1 &&
+ producersBefore.instructionOffsetValue().instructionOffset(0) == PartialEvaluator.AT_METHOD_ENTRY;
+ }
+
+
+ /**
* Returns whether the specified variable must be initialized at the
* specified offset, according to the verifier of the JVM.
*/
@@ -1686,8 +1779,8 @@ implements AttributeVisitor
{
if (DEBUG) System.out.println("Simple partial evaluation for initialization of variable v"+variableIndex+" at ["+initializationOffset+"]");
- // Lazily compute perform simple partial evaluation, the way the
- // JVM preverifier would do it.
+ // Lazily perform simple partial evaluation, the way the JVM
+ // verifier would do it.
simplePartialEvaluator.visitCodeAttribute(clazz, method, codeAttribute);
if (DEBUG) System.out.println("End of simple partial evaluation for initialization of variable v"+variableIndex+" at ["+initializationOffset+"]");
@@ -1695,7 +1788,7 @@ implements AttributeVisitor
// Check if the variable is necessary elsewhere.
for (int offset = 0; offset < codeLength; offset++)
{
- if (isInstructionNecessary(offset))
+ if (partialEvaluator.isTraced(offset))
{
Value producer = partialEvaluator.getVariablesBefore(offset).getProducerValue(variableIndex);
if (producer != null)
@@ -1708,16 +1801,22 @@ implements AttributeVisitor
InstructionOffsetValue simpleProducerOffsets =
simpleProducer.instructionOffsetValue();
- // Does the sophisticated partial evaluation have fewer
- // producers than the simple one?
- // And does the simple partial evaluation point to an
- // initialization of the variable?
- if (producerOffsets.instructionOffsetCount() <
- simpleProducerOffsets.instructionOffsetCount() &&
- isVariableNecessaryAfterAny(producerOffsets, variableIndex) &&
- simpleProducerOffsets.contains(initializationOffset))
+ if (DEBUG)
{
- // Then the initialization is necessary.
+ System.out.println(" ["+offset+"] producers ["+producerOffsets+"], simple producers ["+simpleProducerOffsets+"]");
+ }
+
+ // Is the variable being used without all of its
+ // immediate simple producers being marked?
+ if (isVariableNecessaryAfterAny(producerOffsets, variableIndex) &&
+ !isVariableNecessaryAfterAll(simpleProducerOffsets, variableIndex))
+ {
+ if (DEBUG)
+ {
+ System.out.println(" => initialization of variable v"+variableIndex+" at ["+initializationOffset+"] necessary");
+ }
+
+ // Then the initialization may be necessary.
return true;
}
}
@@ -1726,6 +1825,11 @@ implements AttributeVisitor
}
}
+ if (DEBUG)
+ {
+ System.out.println(" => initialization of variable v"+variableIndex+" at ["+initializationOffset+"] not necessary");
+ }
+
return false;
}
@@ -1749,7 +1853,7 @@ implements AttributeVisitor
/**
* Returns whether the specified variable is ever necessary after any
- * instruction in the specified block.
+ * instructions in the specified block.
*/
private boolean isVariableNecessaryAfterAny(int startOffset,
int endOffset,
@@ -1769,7 +1873,7 @@ implements AttributeVisitor
/**
* Returns whether the specified variable is ever necessary after any
- * instruction in the specified set of instructions offsets.
+ * instructions in the specified set of instructions offsets.
*/
private boolean isVariableNecessaryAfterAny(InstructionOffsetValue instructionOffsetValue,
int variableIndex)
@@ -1789,6 +1893,28 @@ implements AttributeVisitor
}
+ /**
+ * Returns whether the specified variable is ever necessary after all
+ * instructions in the specified set of instructions offsets.
+ */
+ private boolean isVariableNecessaryAfterAll(InstructionOffsetValue instructionOffsetValue,
+ int variableIndex)
+ {
+ int count = instructionOffsetValue.instructionOffsetCount();
+
+ for (int index = 0; index < count; index++)
+ {
+ if (!isVariableNecessaryAfter(instructionOffsetValue.instructionOffset(index),
+ variableIndex))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
private boolean isVariableNecessaryAfter(int instructionOffset,
int variableIndex)
{
@@ -1797,6 +1923,36 @@ implements AttributeVisitor
}
+ /**
+ * Marks the stack entries after the given offsets.
+ * @param instructionOffsets the offsets of the stack entries to be marked.
+ * @param stackIndex the index of the stack entries to be marked
+ * (counting from the bottom).
+ */
+ private void markStackEntriesAfter(InstructionOffsetValue instructionOffsets,
+ int stackIndex)
+ {
+ if (instructionOffsets != null)
+ {
+ int offsetCount = instructionOffsets.instructionOffsetCount();
+ for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++)
+ {
+ // Make sure the stack entry and the instruction are marked
+ // at the producing offset.
+ int offset = instructionOffsets.instructionOffset(offsetIndex);
+
+ markStackEntryAfter(offset, stackIndex);
+ }
+ }
+ }
+
+
+ /**
+ * Marks the stack entry after the given offset.
+ * @param instructionOffset the offset of the stack entry to be marked.
+ * @param stackIndex the index of the stack entry to be marked
+ * (counting from the bottom).
+ */
private void markStackEntryAfter(int instructionOffset,
int stackIndex)
{
@@ -1814,6 +1970,13 @@ implements AttributeVisitor
}
+ /**
+ * Returns whether any of the stack entries after the given offsets are
+ * necessary.
+ * @param instructionOffsets the offsets of the stack entries to be checked.
+ * @param stackIndex the index of the stack entries to be checked
+ * (counting from the bottom).
+ */
private boolean isAnyStackEntryNecessaryAfter(InstructionOffsetValue instructionOffsets,
int stackIndex)
{
@@ -1831,6 +1994,13 @@ implements AttributeVisitor
}
+ /**
+ * Returns whether any of the stack entries after the given offset are
+ * necessary.
+ * @param instructionOffset the offset of the stack entry to be checked.
+ * @param stackIndex the index of the stack entry to be checked
+ * (counting from the bottom).
+ */
private boolean isStackEntryNecessaryAfter(int instructionOffset,
int stackIndex)
{
diff --git a/src/proguard/optimize/evaluation/EvaluationSimplifier.java b/src/proguard/optimize/evaluation/EvaluationSimplifier.java
index 0c3a9c7..b207108 100644
--- a/src/proguard/optimize/evaluation/EvaluationSimplifier.java
+++ b/src/proguard/optimize/evaluation/EvaluationSimplifier.java
@@ -2,7 +2,7 @@
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
- * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu)
+ * Copyright (c) 2002-2011 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
@@ -43,6 +43,9 @@ extends SimplifiedVisitor
implements AttributeVisitor,
InstructionVisitor
{
+ private static int POS_ZERO_FLOAT_BITS = Float.floatToIntBits(0.0f);
+ private static long POS_ZERO_DOUBLE_BITS = Double.doubleToLongBits(0.0);
+
//*
private static final boolean DEBUG = false;
/*/
@@ -559,10 +562,11 @@ implements AttributeVisitor,
Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
if (pushedValue.isParticular())
{
+ // Make sure to distinguish between +0.0 and -0.0.
float value = pushedValue.floatValue().value();
- if (value == 0f ||
- value == 1f ||
- value == 2f)
+ if (value == 0.0f && Float.floatToIntBits(value) == POS_ZERO_FLOAT_BITS ||
+ value == 1.0f ||
+ value == 2.0f)
{
replaceConstantPushInstruction(clazz,
offset,
@@ -627,8 +631,9 @@ implements AttributeVisitor,
Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
if (pushedValue.isParticular())
{
+ // Make sure to distinguish between +0.0 and -0.0.
double value = pushedValue.doubleValue().value();
- if (value == 0.0 ||
+ if (value == 0.0 && Double.doubleToLongBits(value) == POS_ZERO_DOUBLE_BITS ||
value == 1.0)
{
replaceConstantPushInstruction(clazz,
diff --git a/src/proguard/optimize/evaluation/LivenessAnalyzer.java b/src/proguard/optimize/evaluation/LivenessAnalyzer.java
index 9915027..747f993 100644
--- a/src/proguard/optimize/evaluation/LivenessAnalyzer.java
+++ b/src/proguard/optimize/evaluation/LivenessAnalyzer.java
@@ -2,7 +2,7 @@
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
- * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu)
+ * Copyright (c) 2002-2011 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
@@ -79,6 +79,16 @@ implements AttributeVisitor,
/**
+ * Returns whether the instruction at the given offset has ever been
+ * executed during the partial evaluation.
+ */
+ public boolean isTraced(int instructionOffset)
+ {
+ return partialEvaluator.isTraced(instructionOffset);
+ }
+
+
+ /**
* Returns whether the specified variable is alive before the instruction
* at the given offset.
*/
diff --git a/src/proguard/optimize/evaluation/LoadingInvocationUnit.java b/src/proguard/optimize/evaluation/LoadingInvocationUnit.java
index 8379c57..578ece5 100644
--- a/src/proguard/optimize/evaluation/LoadingInvocationUnit.java
+++ b/src/proguard/optimize/evaluation/LoadingInvocationUnit.java
@@ -2,7 +2,7 @@
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
- * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu)
+ * Copyright (c) 2002-2011 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
@@ -35,9 +35,9 @@ import proguard.evaluation.value.*;
public class LoadingInvocationUnit
extends BasicInvocationUnit
{
- private boolean loadFieldValues;
- private boolean loadMethodParameterValues;
- private boolean loadMethodReturnValues;
+ private final boolean loadFieldValues;
+ private final boolean loadMethodParameterValues;
+ private final boolean loadMethodReturnValues;
/**
@@ -84,8 +84,6 @@ extends BasicInvocationUnit
value.isParticular())
{
return value;
-// // Make sure the value is refreshed.
-// return refresh(value);
}
}
}
@@ -110,8 +108,6 @@ extends BasicInvocationUnit
value.isParticular())
{
return value;
-// // Make sure the value is refreshed.
-// return refresh(value);
}
}
}
@@ -134,8 +130,6 @@ extends BasicInvocationUnit
value.isParticular())
{
return value;
-// // Make sure the value is refreshed.
-// return refresh(value);
}
}
@@ -163,8 +157,6 @@ extends BasicInvocationUnit
value.isParticular())
{
return value;
-// // Make sure the value is refreshed.
-// return refresh(value);
}
}
}
@@ -173,8 +165,8 @@ extends BasicInvocationUnit
refConstant,
type);
}
-//
-//
+
+
// // Small utility methods.
//
// private Value refresh(Value value)
diff --git a/src/proguard/optimize/evaluation/PartialEvaluator.java b/src/proguard/optimize/evaluation/PartialEvaluator.java
index 5790a36..b2776d1 100644
--- a/src/proguard/optimize/evaluation/PartialEvaluator.java
+++ b/src/proguard/optimize/evaluation/PartialEvaluator.java
@@ -2,7 +2,7 @@
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
- * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu)
+ * Copyright (c) 2002-2011 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
@@ -31,6 +31,8 @@ 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.
@@ -68,7 +70,6 @@ implements AttributeVisitor,
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 int[] initializedVariables = new int[ClassConstants.TYPICAL_CODE_LENGTH];
private boolean evaluateExceptions;
private final BasicBranchUnit branchUnit;
@@ -189,6 +190,47 @@ implements AttributeVisitor,
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;
@@ -212,7 +254,8 @@ implements AttributeVisitor,
TracedStack stack = new TracedStack(codeAttribute.u2maxStack);
// Initialize the reusable arrays and variables.
- initializeVariables(clazz, method, codeAttribute, variables, stack);
+ initializeArrays(codeAttribute);
+ initializeParameters(clazz, method, codeAttribute, variables);
// Find all instruction offsets,...
codeAttribute.accept(clazz, method, branchTargetFinder);
@@ -249,12 +292,6 @@ implements AttributeVisitor,
if (isTraced(offset))
{
- int variableIndex = initializedVariable(offset);
- if (variableIndex >= 0)
- {
- System.out.println(" is initializing variable v"+variableIndex);
- }
-
int initializationOffset = branchTargetFinder.initializationOffset(offset);
if (initializationOffset != NONE)
{
@@ -479,16 +516,6 @@ implements AttributeVisitor,
}
- /**
- * Returns the variable that is initialized at the given instruction offset,
- * or <code>NONE</code> if no variable was initialized.
- */
- public int initializedVariable(int instructionOffset)
- {
- return initializedVariables[instructionOffset];
- }
-
-
// Utility methods to evaluate instruction blocks.
/**
@@ -702,9 +729,6 @@ implements AttributeVisitor,
// Reset the trace value.
InstructionOffsetValue traceValue = InstructionOffsetValue.EMPTY_VALUE;
- // Reset the initialization flag.
- variables.resetInitialization();
-
// Note that the instruction is only volatile.
Instruction instruction = InstructionFactory.create(code, instructionOffset);
@@ -743,9 +767,6 @@ implements AttributeVisitor,
throw ex;
}
- // Collect the offsets of the instructions whose results were used.
- initializedVariables[instructionOffset] = variables.getInitializationIndex();
-
// Collect the branch targets from the branch unit.
InstructionOffsetValue branchTargets = branchUnit.getTraceBranchTargets();
int branchTargetCount = branchTargets.instructionOffsetCount();
@@ -896,11 +917,7 @@ implements AttributeVisitor,
subroutinePartialEvaluator = new PartialEvaluator(this);
- subroutinePartialEvaluator.initializeVariables(clazz,
- method,
- codeAttribute,
- variables,
- stack);
+ subroutinePartialEvaluator.initializeArrays(codeAttribute);
}
// Evaluate the subroutine.
@@ -952,13 +969,12 @@ implements AttributeVisitor,
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];
- initializedVariables[offset] = other.initializedVariables[offset];
+ 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
{
@@ -968,7 +984,6 @@ implements AttributeVisitor,
stacksAfter[offset] .generalize(other.stacksAfter[offset]);
//generalizedContexts[offset]
evaluationCounts[offset] += other.evaluationCounts[offset];
- //initializedVariables[offset]
}
}
}
@@ -1094,11 +1109,7 @@ implements AttributeVisitor,
/**
* Initializes the data structures for the variables, stack, etc.
*/
- private void initializeVariables(Clazz clazz,
- Method method,
- CodeAttribute codeAttribute,
- TracedVariables variables,
- TracedStack stack)
+ private void initializeArrays(CodeAttribute codeAttribute)
{
int codeLength = codeAttribute.u4codeLength;
@@ -1106,33 +1117,25 @@ implements AttributeVisitor,
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];
- initializedVariables = new int[codeLength];
-
- // Reset the arrays.
- for (int index = 0; index < codeLength; index++)
- {
- initializedVariables[index] = NONE;
- }
+ 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++)
{
- branchOriginValues[index] = null;
- branchTargetValues[index] = null;
- generalizedContexts[index] = false;
- evaluationCounts[index] = 0;
- initializedVariables[index] = NONE;
-
if (variablesBefore[index] != null)
{
variablesBefore[index].reset(codeAttribute.u2maxLocals);
@@ -1154,7 +1157,17 @@ implements AttributeVisitor,
}
}
}
+ }
+
+ /**
+ * 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);
diff --git a/src/proguard/optimize/evaluation/StoringInvocationUnit.java b/src/proguard/optimize/evaluation/StoringInvocationUnit.java
index bcbb69f..010dab0 100644
--- a/src/proguard/optimize/evaluation/StoringInvocationUnit.java
+++ b/src/proguard/optimize/evaluation/StoringInvocationUnit.java
@@ -2,7 +2,7 @@
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
- * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu)
+ * Copyright (c) 2002-2011 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
diff --git a/src/proguard/optimize/evaluation/TracedBranchUnit.java b/src/proguard/optimize/evaluation/TracedBranchUnit.java
index fa5bb79..b7dd469 100644
--- a/src/proguard/optimize/evaluation/TracedBranchUnit.java
+++ b/src/proguard/optimize/evaluation/TracedBranchUnit.java
@@ -2,7 +2,7 @@
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
- * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu)
+ * Copyright (c) 2002-2011 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
diff --git a/src/proguard/optimize/evaluation/VariableOptimizer.java b/src/proguard/optimize/evaluation/VariableOptimizer.java
index b3ae81c..a824481 100644
--- a/src/proguard/optimize/evaluation/VariableOptimizer.java
+++ b/src/proguard/optimize/evaluation/VariableOptimizer.java
@@ -2,7 +2,7 @@
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
- * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu)
+ * Copyright (c) 2002-2011 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
@@ -21,10 +21,10 @@
package proguard.optimize.evaluation;
import proguard.classfile.*;
+import proguard.classfile.attribute.visitor.*;
+import proguard.classfile.editor.*;
import proguard.classfile.visitor.MemberVisitor;
import proguard.classfile.attribute.*;
-import proguard.classfile.attribute.visitor.AttributeVisitor;
-import proguard.classfile.editor.VariableRemapper;
import proguard.classfile.util.*;
/**
@@ -35,7 +35,9 @@ import proguard.classfile.util.*;
*/
public class VariableOptimizer
extends SimplifiedVisitor
-implements AttributeVisitor
+implements AttributeVisitor,
+ LocalVariableInfoVisitor,
+ LocalVariableTypeInfoVisitor
{
//*
private static final boolean DEBUG = false;
@@ -51,6 +53,7 @@ implements AttributeVisitor
private final LivenessAnalyzer livenessAnalyzer = new LivenessAnalyzer();
private final VariableRemapper variableRemapper = new VariableRemapper();
+ private VariableCleaner variableCleaner = new VariableCleaner();
private int[] variableMap = new int[ClassConstants.TYPICAL_VARIABLES_SIZE];
@@ -101,6 +104,11 @@ implements AttributeVisitor
// Analyze the liveness of the variables in the code.
livenessAnalyzer.visitCodeAttribute(clazz, method, codeAttribute);
+ // Trim the variables in the local variable tables, because even
+ // clipping the tables individually may leave some inconsistencies
+ // between them.
+ codeAttribute.attributesAccept(clazz, method, this);
+
int startIndex =
(method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ||
reuseThis ? 0 : 1;
@@ -142,23 +150,19 @@ implements AttributeVisitor
}
}
- // Remap the variables.
+ // Have we been able to remap any variables?
if (remapping)
{
if (DEBUG)
{
- System.out.println("Remapping variables:");
- System.out.println(" Class "+ ClassUtil.externalClassName(clazz.getName()));
- System.out.println(" Method "+ClassUtil.externalFullMethodDescription(clazz.getName(),
- 0,
- method.getName(clazz),
- method.getDescriptor(clazz)));
- for (int index = 0; index < variableSize; index++)
+ System.out.println("VariableOptimizer: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz));
+ for (int index= 0; index < variableSize; index++)
{
- System.out.println(" ["+index+"] -> ["+variableMap[index]+"]");
+ System.out.println(" v"+index+" -> "+variableMap[index]);
}
}
+ // Remap the variables.
variableRemapper.setVariableMap(variableMap);
variableRemapper.visitCodeAttribute(clazz, method, codeAttribute);
@@ -168,6 +172,59 @@ implements AttributeVisitor
method.accept(clazz, extraVariableMemberVisitor);
}
}
+ else
+ {
+ // Just clean up any empty variables.
+ variableCleaner.visitCodeAttribute(clazz, method, codeAttribute);
+ }
+ }
+
+
+ public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
+ {
+ // Trim the variables in the local variable table.
+ localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
+ }
+
+
+ public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
+ {
+ // Trim the variables in the local variable type table.
+ localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
+ }
+
+
+ // Implementations for LocalVariableInfoVisitor.
+
+ public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
+ {
+ // Trim the local variable to the instructions at which it is alive.
+ int variable = localVariableInfo.u2index;
+ int startPC = localVariableInfo.u2startPC;
+ int endPC = startPC + localVariableInfo.u2length;
+
+ startPC = firstLiveness(startPC, endPC, variable);
+ endPC = lastLiveness(startPC, endPC, variable);
+
+ localVariableInfo.u2startPC = startPC;
+ localVariableInfo.u2length = endPC - startPC;
+ }
+
+
+ // Implementations for LocalVariableTypeInfoVisitor.
+
+ public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
+ {
+ // Trim the local variable type to the instructions at which it is alive.
+ int variable = localVariableTypeInfo.u2index;
+ int startPC = localVariableTypeInfo.u2startPC;
+ int endPC = startPC + localVariableTypeInfo.u2length;
+
+ startPC = firstLiveness(startPC, endPC, variable);
+ endPC = lastLiveness(startPC, endPC, variable);
+
+ localVariableTypeInfo.u2startPC = startPC;
+ localVariableTypeInfo.u2length = endPC - startPC;
}
@@ -241,4 +298,48 @@ implements AttributeVisitor
}
}
}
+
+
+ /**
+ * Returns the first instruction offset between the given offsets at which
+ * the given variable goes alive.
+ */
+ private int firstLiveness(int startOffset, int endOffset, int variableIndex)
+ {
+ for (int offset = startOffset; offset < endOffset; offset++)
+ {
+ if (livenessAnalyzer.isTraced(offset) &&
+ livenessAnalyzer.isAliveBefore(offset, variableIndex))
+ {
+ return offset;
+ }
+ }
+
+ return endOffset;
+ }
+
+
+ /**
+ * Returns the last instruction offset between the given offsets before
+ * which the given variable is still alive.
+ */
+ private int lastLiveness(int startOffset, int endOffset, int variableIndex)
+ {
+ int previousOffset = endOffset;
+
+ for (int offset = endOffset-1; offset >= startOffset; offset--)
+ {
+ if (livenessAnalyzer.isTraced(offset))
+ {
+ if (livenessAnalyzer.isAliveBefore(offset, variableIndex))
+ {
+ return previousOffset;
+ }
+
+ previousOffset = offset;
+ }
+ }
+
+ return endOffset;
+ }
}