proguard.optimize.evaluation.InitializationFinder Maven / Gradle / Ivy
/*
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
* Copyright (c) 2002-2018 GuardSquare NV
*
* 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.instruction.InstructionFactory;
import proguard.classfile.instruction.visitor.InstructionVisitor;
import proguard.classfile.util.SimplifiedVisitor;
import proguard.evaluation.BasicInvocationUnit;
import proguard.evaluation.value.*;
import proguard.util.ArrayUtil;
/**
* This AttributeVisitor links 'new' instructions and their corresponding
* initializers in the CodeAttribute objects that it visits.
*
* @author Eric Lafortune
*/
public class InitializationFinder
extends SimplifiedVisitor
implements AttributeVisitor,
InstructionVisitor
{
//*
private static final boolean DEBUG = false;
/*/
private static boolean DEBUG = System.getProperty("if") != null;
//*/
public static final int NONE = -1;
private final PartialEvaluator partialEvaluator;
private final boolean runPartialEvaluator;
private int superInitializationOffset;
private int[] initializationOffsets = new int[ClassConstants.TYPICAL_CODE_LENGTH];
private InstructionOffsetValue[] uninitializedOffsets = new InstructionOffsetValue[ClassConstants.TYPICAL_CODE_LENGTH];
/**
* Creates a new InitializationFinder.
*/
public InitializationFinder()
{
this(new ReferenceTracingValueFactory(new BasicValueFactory()));
}
/**
* Creates a new InitializationFinder. This private constructor gets around
* the constraint that it's not allowed to add statements before calling
* 'this'.
*/
private InitializationFinder(ReferenceTracingValueFactory referenceTracingValueFactory)
{
this(new PartialEvaluator(referenceTracingValueFactory,
new ReferenceTracingInvocationUnit(new BasicInvocationUnit(referenceTracingValueFactory)),
true,
referenceTracingValueFactory),
true);
}
/**
* Creates a new InitializationFinder that will use the given partial
* evaluator.
* @param partialEvaluator the evaluator to be used for the analysis.
* @param runPartialEvaluator specifies whether to run this evaluator on
* every code attribute that is visited.
*/
public InitializationFinder(PartialEvaluator partialEvaluator,
boolean runPartialEvaluator)
{
this.partialEvaluator = partialEvaluator;
this.runPartialEvaluator = runPartialEvaluator;
}
/**
* Returns whether the method is an instance initializer, in the
* CodeAttribute that was visited most recently.
*/
public boolean isInitializer()
{
return superInitializationOffset != NONE;
}
/**
* Returns the instruction offset at which this initializer is calling
* the "super" or "this" initializer method, or NONE
if it is
* not an initializer.
*/
public int superInitializationOffset()
{
return superInitializationOffset;
}
// /**
// * Returns whether the instruction at the given offset is a 'new'
// * instruction.
// */
// public boolean isNew(int offset)
// {
// return initializationOffsets[offset] != NONE;
// }
//
//
// /**
// * Returns the instruction offset at which the object instance that is
// * created at the given 'new' instruction offset is initialized, or
// * NONE
if it is not being created.
// */
// public int initializationOffset(int creationOffset)
// {
// return initializationOffsets[creationOffset];
// }
/**
* Returns the 'new' instruction offset at which the object instance is
* created that is initialized at the given offset.
*/
public int creationOffset(int initializationOffset)
{
return creationOffsetValue(initializationOffset).instructionOffset(0);
}
/**
* Returns whether the specified stack entry is initialized.
*/
public boolean isInitializedBefore(int offset, int stackEntryIndexBottom)
{
InstructionOffsetValue creationOffsetValue =
creationOffsetValue(offset, stackEntryIndexBottom);
return isInitializedBefore(offset, creationOffsetValue);
}
/**
* Returns whether the given creation offset is initialized before the given
* offset.
*/
public boolean isInitializedBefore(int offset,
InstructionOffsetValue creationOffsetValue)
{
return !uninitializedOffsets[offset].contains(creationOffsetValue.instructionOffset(0));
}
/**
* Returns whether the instruction at the given offset is the special
* invocation of an instance initializer.
*/
public boolean isInitializer(int offset)
{
return partialEvaluator.isInitializer(offset);
}
// 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");
int codeLength = codeAttribute.u4codeLength;
superInitializationOffset = NONE;
// Make sure the global arrays are sufficiently large.
initializationOffsets = ArrayUtil.ensureArraySize(initializationOffsets, codeLength, NONE);
uninitializedOffsets = ArrayUtil.ensureArraySize(uninitializedOffsets,codeLength, null);
// Evaluate the method.
if (runPartialEvaluator)
{
partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute);
}
// Loop over all instructions. This is sufficient, because the JVM
// specifications don't allow uninitialized instances on the stack or
// in variables when branching backward. JVMs without preverification
// and the Dalvik VM do allow it in practice.
InstructionOffsetValue currentUninitializedOffsets = method.getName(clazz).equals(ClassConstants.METHOD_NAME_INIT) ?
new InstructionOffsetValue(InstructionOffsetValue.METHOD_PARAMETER) :
InstructionOffsetValue.EMPTY_VALUE;
for (int offset = 0; offset < codeLength; offset++)
{
if (partialEvaluator.isTraced(offset))
{
// Exception handlers start without uninitialized instances
// (on the stack or in variables).
if (partialEvaluator.isExceptionHandler(offset))
{
currentUninitializedOffsets = InstructionOffsetValue.EMPTY_VALUE;
}
// Check if the uninitialized creation offsets have been set
// before (because of a forward branch).
if (uninitializedOffsets[offset] != null)
{
// Continue using them.
currentUninitializedOffsets = uninitializedOffsets[offset];
}
else
{
uninitializedOffsets[offset] = currentUninitializedOffsets;
}
// Is it a 'new' instruction?
if (partialEvaluator.isCreation(offset))
{
// Add its offset to the current list.
currentUninitializedOffsets =
currentUninitializedOffsets.add(offset);
}
// Is it an instance initialization?
else if (partialEvaluator.isInitializer(offset))
{
// Remove its creation offset from the current list.
InstructionOffsetValue creationOffsetValue =
creationOffsetValue(offset);
int creationOffset =
creationOffsetValue.instructionOffset(0);
if (creationOffsetValue.isMethodParameter(0))
{
// Remember the super initialization offset of the
// initializer method.
superInitializationOffset = offset;
}
else
{
// Remember the instance initialization for the 'new'
// instruction.
initializationOffsets[creationOffset] = offset;
}
currentUninitializedOffsets =
currentUninitializedOffsets.remove(creationOffset);
}
// Propagate the uninitialized creation offsets to the forward
// branch targets, if any.
InstructionOffsetValue branchTargets =
partialEvaluator.branchTargets(offset);
if (branchTargets != null)
{
for (int branchIndex = 0; branchIndex < branchTargets.instructionOffsetCount(); branchIndex++)
{
int branchOffset = branchTargets.instructionOffset(branchIndex);
if (branchOffset > offset)
{
uninitializedOffsets[branchOffset] = currentUninitializedOffsets;
}
}
currentUninitializedOffsets = InstructionOffsetValue.EMPTY_VALUE;
}
}
}
if (DEBUG)
{
System.out.println();
System.out.println("InitializationFinder: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz));
for (int offset = 0; offset < codeLength; offset++)
{
if (partialEvaluator.isInstruction(offset))
{
System.out.println((initializationOffsets[offset] >= 0 ? "i"+initializationOffsets[offset] : " ") +
(uninitializedOffsets[offset] != null &&
uninitializedOffsets[offset].instructionOffsetCount() > 0 ? " u"+uninitializedOffsets[offset] : " ") +
(isInitializer(offset) ? " "+creationOffsetValue(offset) : " ") + " " +
InstructionFactory.create(codeAttribute.code, offset).toString(offset));
}
}
}
}
// Small utility methods.
/**
* Returns the 'new' instruction offset value (or method parameter) at
* which the object instance is created that is initialized at the given
* offset.
*/
private InstructionOffsetValue creationOffsetValue(int initializationOffset)
{
int stackEntryIndexBottom =
partialEvaluator.getStackAfter(initializationOffset).size();
return creationOffsetValue(initializationOffset, stackEntryIndexBottom);
}
/**
* Returns the 'new' instruction offset value (or method parameter) of
* the specified stack entry.
*/
private InstructionOffsetValue creationOffsetValue(int instructionOffset,
int stackEntryIndexBottom)
{
// Get the reference value of the new instance.
ReferenceValue newReferenceValue =
partialEvaluator.getStackBefore(instructionOffset).getBottom(stackEntryIndexBottom).referenceValue();
// It's a traced reference.
TracedReferenceValue tracedReferenceValue =
(TracedReferenceValue)newReferenceValue;
// Get the trace value.
return tracedReferenceValue.getTraceValue().instructionOffsetValue();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy