blob: 30cc90674745e8ba9f25bebf2e6d79e4a61afae0 [file] [log] [blame]
/*
* Copyright 2013, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib2.analysis;
import org.jf.dexlib2.iface.instruction.*;
import org.jf.dexlib2.iface.reference.MethodReference;
import org.jf.dexlib2.iface.reference.Reference;
import org.jf.util.ExceptionWithContext;
import javax.annotation.Nonnull;
import java.util.*;
public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> {
/**
* The actual instruction
*/
protected Instruction instruction;
/**
* The index of the instruction, where the first instruction in the method is at index 0, and so on
*/
protected final int instructionIndex;
/**
* Instructions that can pass on execution to this one during normal execution
*/
protected final TreeSet<AnalyzedInstruction> predecessors = new TreeSet<AnalyzedInstruction>();
/**
* Instructions that can execution could pass on to next during normal execution
*/
protected final LinkedList<AnalyzedInstruction> successors = new LinkedList<AnalyzedInstruction>();
/**
* This contains the register types *before* the instruction has executed
*/
protected final RegisterType[] preRegisterMap;
/**
* This contains the register types *after* the instruction has executed
*/
protected final RegisterType[] postRegisterMap;
/**
* When deodexing, we might need to deodex this instruction multiple times, when we merge in new register
* information. When this happens, we need to restore the original (odexed) instruction, so we can deodex it again
*/
protected final Instruction originalInstruction;
public AnalyzedInstruction(Instruction instruction, int instructionIndex, int registerCount) {
this.instruction = instruction;
this.originalInstruction = instruction;
this.instructionIndex = instructionIndex;
this.postRegisterMap = new RegisterType[registerCount];
this.preRegisterMap = new RegisterType[registerCount];
RegisterType unknown = RegisterType.getRegisterType(RegisterType.UNKNOWN, null);
for (int i=0; i<registerCount; i++) {
preRegisterMap[i] = unknown;
postRegisterMap[i] = unknown;
}
}
public int getInstructionIndex() {
return instructionIndex;
}
public int getPredecessorCount() {
return predecessors.size();
}
public SortedSet<AnalyzedInstruction> getPredecessors() {
return Collections.unmodifiableSortedSet(predecessors);
}
protected boolean addPredecessor(AnalyzedInstruction predecessor) {
return predecessors.add(predecessor);
}
protected void addSuccessor(AnalyzedInstruction successor) {
successors.add(successor);
}
protected void setDeodexedInstruction(Instruction instruction) {
assert originalInstruction.getOpcode().odexOnly();
this.instruction = instruction;
}
protected void restoreOdexedInstruction() {
assert originalInstruction.getOpcode().odexOnly();
instruction = originalInstruction;
}
public int getSuccessorCount() {
return successors.size();
}
public List<AnalyzedInstruction> getSuccesors() {
return Collections.unmodifiableList(successors);
}
public Instruction getInstruction() {
return instruction;
}
public Instruction getOriginalInstruction() {
return originalInstruction;
}
/**
* Is this instruction a "beginning instruction". A beginning instruction is defined to be an instruction
* that can be the first successfully executed instruction in the method. The first instruction is always a
* beginning instruction. If the first instruction can throw an exception, and is covered by a try block, then
* the first instruction of any exception handler for that try block is also a beginning instruction. And likewise,
* if any of those instructions can throw an exception and are covered by try blocks, the first instruction of the
* corresponding exception handler is a beginning instruction, etc.
*
* To determine this, we simply check if the first predecessor is the fake "StartOfMethod" instruction, which has
* an instruction index of -1.
* @return a boolean value indicating whether this instruction is a beginning instruction
*/
public boolean isBeginningInstruction() {
//if this instruction has no predecessors, it is either the fake "StartOfMethod" instruction or it is an
//unreachable instruction.
if (predecessors.size() == 0) {
return false;
}
if (predecessors.first().instructionIndex == -1) {
return true;
}
return false;
}
/*
* Merges the given register type into the specified pre-instruction register, and also sets the post-instruction
* register type accordingly if it isn't a destination register for this instruction
* @param registerNumber Which register to set
* @param registerType The register type
* @returns true If the post-instruction register type was changed. This might be false if either the specified
* register is a destination register for this instruction, or if the pre-instruction register type didn't change
* after merging in the given register type
*/
protected boolean mergeRegister(int registerNumber, RegisterType registerType, BitSet verifiedInstructions) {
assert registerNumber >= 0 && registerNumber < postRegisterMap.length;
assert registerType != null;
RegisterType oldRegisterType = preRegisterMap[registerNumber];
RegisterType mergedRegisterType = oldRegisterType.merge(registerType);
if (mergedRegisterType.equals(oldRegisterType)) {
return false;
}
preRegisterMap[registerNumber] = mergedRegisterType;
verifiedInstructions.clear(instructionIndex);
if (!setsRegister(registerNumber)) {
postRegisterMap[registerNumber] = mergedRegisterType;
return true;
}
return false;
}
/**
* Iterates over the predecessors of this instruction, and merges all the post-instruction register types for the
* given register. Any dead, unreachable, or odexed predecessor is ignored
* @param registerNumber the register number
* @return The register type resulting from merging the post-instruction register types from all predecessors
*/
protected RegisterType mergePreRegisterTypeFromPredecessors(int registerNumber) {
RegisterType mergedRegisterType = null;
for (AnalyzedInstruction predecessor: predecessors) {
RegisterType predecessorRegisterType = predecessor.postRegisterMap[registerNumber];
assert predecessorRegisterType != null;
mergedRegisterType = predecessorRegisterType.merge(mergedRegisterType);
}
return mergedRegisterType;
}
/*
* Sets the "post-instruction" register type as indicated.
* @param registerNumber Which register to set
* @param registerType The "post-instruction" register type
* @returns true if the given register type is different than the existing post-instruction register type
*/
protected boolean setPostRegisterType(int registerNumber, RegisterType registerType) {
assert registerNumber >= 0 && registerNumber < postRegisterMap.length;
assert registerType != null;
RegisterType oldRegisterType = postRegisterMap[registerNumber];
if (oldRegisterType.equals(registerType)) {
return false;
}
postRegisterMap[registerNumber] = registerType;
return true;
}
protected boolean isInvokeInit() {
if (instruction == null || !instruction.getOpcode().canInitializeReference()) {
return false;
}
ReferenceInstruction instruction = (ReferenceInstruction)this.instruction;
Reference reference = instruction.getReference();
if (reference instanceof MethodReference) {
return ((MethodReference)reference).getName().equals("<init>");
}
return false;
}
public boolean setsRegister() {
return instruction.getOpcode().setsRegister();
}
public boolean setsWideRegister() {
return instruction.getOpcode().setsWideRegister();
}
public boolean setsRegister(int registerNumber) {
//When constructing a new object, the register type will be an uninitialized reference after the new-instance
//instruction, but becomes an initialized reference once the <init> method is called. So even though invoke
//instructions don't normally change any registers, calling an <init> method will change the type of its
//object register. If the uninitialized reference has been copied to other registers, they will be initialized
//as well, so we need to check for that too
if (isInvokeInit()) {
int destinationRegister;
if (instruction instanceof FiveRegisterInstruction) {
destinationRegister = ((FiveRegisterInstruction)instruction).getRegisterC();
} else {
assert instruction instanceof RegisterRangeInstruction;
RegisterRangeInstruction rangeInstruction = (RegisterRangeInstruction)instruction;
assert rangeInstruction.getRegisterCount() > 0;
destinationRegister = rangeInstruction.getStartRegister();
}
if (registerNumber == destinationRegister) {
return true;
}
RegisterType preInstructionDestRegisterType = getPreInstructionRegisterType(registerNumber);
if (preInstructionDestRegisterType.category != RegisterType.UNINIT_REF &&
preInstructionDestRegisterType.category != RegisterType.UNINIT_THIS) {
return false;
}
//check if the uninit ref has been copied to another register
if (getPreInstructionRegisterType(registerNumber).equals(preInstructionDestRegisterType)) {
return true;
}
return false;
}
if (!setsRegister()) {
return false;
}
int destinationRegister = getDestinationRegister();
if (registerNumber == destinationRegister) {
return true;
}
if (setsWideRegister() && registerNumber == (destinationRegister + 1)) {
return true;
}
return false;
}
public int getDestinationRegister() {
if (!this.instruction.getOpcode().setsRegister()) {
throw new ExceptionWithContext("Cannot call getDestinationRegister() for an instruction that doesn't " +
"store a value");
}
return ((OneRegisterInstruction)instruction).getRegisterA();
}
public int getRegisterCount() {
return postRegisterMap.length;
}
@Nonnull
public RegisterType getPostInstructionRegisterType(int registerNumber) {
return postRegisterMap[registerNumber];
}
@Nonnull
public RegisterType getPreInstructionRegisterType(int registerNumber) {
return preRegisterMap[registerNumber];
}
public int compareTo(AnalyzedInstruction analyzedInstruction) {
if (instructionIndex < analyzedInstruction.instructionIndex) {
return -1;
} else if (instructionIndex == analyzedInstruction.instructionIndex) {
return 0;
} else {
return 1;
}
}
}