package org.ow2.asmdex;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.ow2.asmdex.instruction.InstructionFormat10T;
import org.ow2.asmdex.instruction.InstructionFormat11N;
import org.ow2.asmdex.instruction.InstructionFormat11X;
import org.ow2.asmdex.instruction.InstructionFormat12X;
import org.ow2.asmdex.instruction.InstructionFormat20T;
import org.ow2.asmdex.instruction.InstructionFormat21C;
import org.ow2.asmdex.instruction.InstructionFormat21H;
import org.ow2.asmdex.instruction.InstructionFormat21S;
import org.ow2.asmdex.instruction.InstructionFormat21T;
import org.ow2.asmdex.instruction.InstructionFormat22B;
import org.ow2.asmdex.instruction.InstructionFormat22C;
import org.ow2.asmdex.instruction.InstructionFormat22S;
import org.ow2.asmdex.instruction.InstructionFormat22T;
import org.ow2.asmdex.instruction.InstructionFormat22X;
import org.ow2.asmdex.instruction.InstructionFormat23X;
import org.ow2.asmdex.instruction.InstructionFormat30T;
import org.ow2.asmdex.instruction.InstructionFormat31C;
import org.ow2.asmdex.instruction.InstructionFormat31I;
import org.ow2.asmdex.instruction.InstructionFormat31T;
import org.ow2.asmdex.instruction.InstructionFormat32X;
import org.ow2.asmdex.instruction.InstructionFormat35C;
import org.ow2.asmdex.instruction.InstructionFormat3RC;
import org.ow2.asmdex.instruction.InstructionFormat51L;
import org.ow2.asmdex.lowLevelUtils.DexFileReader;
import org.ow2.asmdex.structureCommon.Label;
import org.ow2.asmdex.structureCommon.LocalVariable;
import org.ow2.asmdex.structureReader.ISwitchCase;
import org.ow2.asmdex.structureReader.PackedSwitch;
import org.ow2.asmdex.structureReader.SparseSwitch;
import org.ow2.asmdex.structureReader.TryCatch;
/**
* Class made to read the code of one method of a Dex file, and make a visitor visits its instructions.
* Also parses the debug_info structure to get complementary information (line numbers, local variables...).
* <br/><br/>
* Should only be used by ApplicationReader.
* <br/><br/>
* The NOP instructions are ignored if we reach any Pseudo Instruction such as Packed/Sparse Switch or
* Fill Array Data, so that NOP don't stack up if we transform an already generated dex file. Meeting any
* other instruction will stop this behavior.
*
* @author Julien Névo
*/
public class MethodCodeReader {
/**
* The Method Visitor.
*/
protected MethodVisitor methodVisitor;
/**
* The reader of the Dex file.
*/
protected DexFileReader dexFile;
/**
* Offset of the code of the method inside the Dex file. May be 0 if the method is abstract or native.
*/
protected int codeOffset;
/**
* Indicates if the debug information must be skipped.
*/
protected boolean skipDebug;
/**
* Offset in the Dex file of the first opcode of the code of the current method.
*/
protected int instructionsStartOffset;
/**
* Offset in the Dex file of the byte next to the last opcode/data of the code
* of the method.
*/
protected int instructionsEndOffset;
/**
* Map containing the labels located at the given offset (from the beginning
* of the code of the method).
*/
protected HashMap<Integer, Label> labels;
/**
* Map containing the Packed Switch Structures located at the given offset
* (from the beginning of the code of the method). The offset points to the
* packed-switch opcode, NOT the packed-switch format itself.
*/
protected HashMap<Integer, PackedSwitch> packedSwitchStructures;
/**
* Map containing the Sparse Switch Structures located at the given offset
* (from the beginning of the code of the method). The offset points to the
* sparse-switch opcode, NOT the sparse-switch format itself.
*/
protected HashMap<Integer, SparseSwitch> sparseSwitchStructures;
/**
* Map containing lists of the TryCatch Structures located at the given offset
* (from the beginning of the code of the method). The offset points to the
* Try.
*/
protected HashMap<Integer, ArrayList<TryCatch>> listTryCatchStructures;
/**
* Map containing, for each local variable identified by its number (as
* described in the debug_info_item), a list of Local Variables.
* Why a List ? Because a register number can share one or more variables
* (though it doesn't happen often).
* Note that when a Local Variable is started, it is added to the end of the
* list, and all the Restarts/Ends found will be added to this Variable.
*/
protected HashMap<Integer, List<LocalVariable>> localVariableLists;
/**
* Map linking a register to a type if it holds an array. It is useful for the
* NewArray/FillNewArray instructions.
*/
private HashMap<Integer, Integer> registerArrayType = new HashMap<Integer, Integer>();
/*
* The five "state machine" registers of the debug_info_item.
*/
/**
* register of debug state machine : the debug address
*/
protected int debugAddress;
/**
* register of debug state machine : the debug line
*/
protected int debugLine;
/**
* register of debug state machine : the source file
*/
protected String debugSourceFile;
/**
* register of debug state machine : the prologue end
*/
protected boolean debugPrologueEnd;
/**
* register of debug state machine : the epilogue end
*/
protected boolean debugEpilogueBegin;
/**
* Copy of the original debugLine. This is useful, because we make two passes, and need
* to recover the original value for the second.
*/
protected int debugLineAtInitialization;
/**
* Copy of the original debugCurrentOpcodeOffset. This is useful, because we make two
* passes, and need to recover the original value for the second.
*/
protected int debugCurrentOpcodeOffsetAtInitialization;
/**
* Offset on opcodes of the debug_info_item.
*/
protected int debugCurrentOpcodeOffset;
/**
* Indicates if a new Debug Line is emitted.
*/
protected boolean newDebugLineEmitted;
/**
* Last emitted line (DBG_ADVANCE_LINE not counted).
*/
protected int debugEmittedLine;
/**
* Size in bytes of a try_item structure.
*/
private static final int TRY_ITEM_STRUCTURE_SIZE = 8;
/**
* Indicates if the Pseudo Instructions at the end of the method has been reached.
* If true, the NOPs encountered aren't thrown so as not to interpret them and stack them
* when reading once again a generated file. However, it returns to false if others instructions are
* encountered, though it shouldn't happen as we encode try/catch/data array structures at the end
* of the Method. But some compilers may do otherwise.
*/
private boolean hasReachedPseudoInstructions = false;
/**
* Constructor of the MethodCodeReader.
* @param dexFile the Dex file.
* @param methodVisitor visitor to visit the instructions of the code of the method.
* @param codeOffset offset of the code of the method inside the Dex file. May be 0 if
* the method is abstract or native.
* @param skipDebug indicates if the debug information must be skipped.
*/
public MethodCodeReader(DexFileReader dexFile, MethodVisitor methodVisitor,
int codeOffset, boolean skipDebug) {
this.dexFile = dexFile;
this.methodVisitor = methodVisitor;
this.codeOffset = codeOffset;
this.skipDebug = skipDebug;
}
/**
* Visits the code of a method. Also needs to parse the debug_info structure to get
* complementary information (line number, local variables...).
*/
public void visitMethodCode() {
if (codeOffset != 0) { // 0 if abstract or native.
dexFile.seek(codeOffset);
int registerSize = dexFile.ushort();
dexFile.skipShort(); // Skips incomingArgumentsSize.
dexFile.skipShort(); // Skips outgoingArgumentsSize.
int triesSize = dexFile.ushort();
int debugInfoOffset = dexFile.uint();
int instructionsSizeInUShort = dexFile.uint();
instructionsStartOffset = dexFile.getPos();
instructionsEndOffset = instructionsStartOffset + instructionsSizeInUShort * 2;
labels = new HashMap<Integer, Label>();
packedSwitchStructures = new HashMap<Integer, PackedSwitch>();
sparseSwitchStructures = new HashMap<Integer, SparseSwitch>();
listTryCatchStructures = new HashMap<Integer, ArrayList<TryCatch>>(triesSize);
localVariableLists = new HashMap<Integer, List<LocalVariable>>();
// Checks the debug_info_item.
if (!skipDebug && (debugInfoOffset != 0)) {
// Reads the debug parameters. Even if not needed, we read them to get
// past to reach the debug code just after.
dexFile.seek(debugInfoOffset);
debugLineAtInitialization = dexFile.uleb128();
int debugParametersSize = dexFile.uleb128();
// Parses the Parameters, and visits them.
if (debugParametersSize > 0) {
String[] parameters = new String[debugParametersSize];
for (int indexParameter = 0 ; indexParameter < debugParametersSize; indexParameter++) {
int stringIndex = dexFile.uleb128_p1();
String paramName;
if (stringIndex == Opcodes.NO_INDEX_SIGNED) {
paramName = "";
} else {
paramName = dexFile.getStringItemFromStringIndex(stringIndex);
}
parameters[indexParameter] = paramName;
}
methodVisitor.visitParameters(parameters);
}
debugCurrentOpcodeOffsetAtInitialization = dexFile.getPos();
}
methodVisitor.visitCode();
// Contrary to ASM, we visit the Maxs right at the beginning of the code.
methodVisitor.visitMaxs(registerSize, 0);
// Before parsing the code, we skip it in order to reach the
// try/catch handler just after, if there is one.
if (triesSize != 0) {
dexFile.seek(instructionsEndOffset);
// Padding ?
if ((instructionsSizeInUShort % 2) != 0) {
dexFile.ushort();
}
parseTryItemsFormat(dexFile.getPos(), triesSize);
}
// Now we parse the code, in two passes.
// First pass : we only parse the code in order to find the Labels
// (from Goto, Switch...). Visitors are NOT called. So Adapters can't add/remove code either.
parseCodeInstructions(methodVisitor, skipDebug, true);
// Second pass : we parse the code and call all the visitors. Adapters may add/remove code.
parseCodeInstructions(methodVisitor, skipDebug, false);
// Visits the Local Variables.
for (List<LocalVariable> localVariableList : localVariableLists.values()) {
for (LocalVariable localVariable : localVariableList) {
methodVisitor.visitLocalVariable(localVariable.getName(), localVariable.getType(),
localVariable.getSignature(), localVariable.getStart(),
localVariable.getEnds(), localVariable.getRestarts(),
localVariable.getRegister());
}
}
}
// The visitEnd event is thrown by the calling method.
}
/**
* Parses the instructions of the code of the current method, and makes the given visitor
* visit it, unless the parsing is done only to get the Label offsets.
* In the latter case, we build the Label table whenever the following instructions are
* encountered : GOTO, GOTO/16, GOTO/32, PACKED-SWITCH, SPARSE-SWITCH, IF-test, IF-testz.
* The dex file position <i>is</i> modified.
* @param instructionsStartOffset offset in the Dex file of the first opcode of the
* code of the method.
* @param instructionsEndOffset offset in the Dex file of the byte next to the last
* opcode/data of the code of the method.
* @param methodVisitor visitor to call when parsing the code of the method.
* @param skipDebug indicates if the debug information must be skipped.
* @param findLabelsOnly indicates if the parsing is only about getting the Label offsets.
* In this case, no method from the visitor must be called.
*/
private void parseCodeInstructions(MethodVisitor methodVisitor, boolean skipDebug, boolean findLabelsOnly) {
hasReachedPseudoInstructions = false;
// Initializes the debug values.
debugCurrentOpcodeOffset = debugCurrentOpcodeOffsetAtInitialization; // Offset on opcodes of the debug_info_item.
debugAddress = 0;
debugLine = debugLineAtInitialization;
debugEmittedLine = 0;
debugSourceFile = null;
debugPrologueEnd = false;
debugEpilogueBegin = false;
newDebugLineEmitted = false;
// Parses all the instructions.
dexFile.seek(instructionsStartOffset);
while (dexFile.getPos() < instructionsEndOffset) {
int relativeOffset = dexFile.getPos() - instructionsStartOffset;
if (!skipDebug && (debugCurrentOpcodeOffset >= 0)) {
// Parses the Debug Information Item. This will work only if it has information
// to display for the current offset. Note that we don't need to parse
// this structure on the first pass (findLabelOnly).
parseDebugInformationItem(methodVisitor, relativeOffset, findLabelsOnly);
}
// If a Label is present here, visit it. The label table must of course have
// been built before (on the first pass).
if (labels.containsKey(relativeOffset)) {
Label label = labels.get(relativeOffset);
// The line number is valid only if set in the last occurrence
// of the Debug Informations parsing (it implies !skipDebug).
if (newDebugLineEmitted) {
label.setLine(debugEmittedLine);
}
if (!findLabelsOnly) {
methodVisitor.visitLabel(label);
}
}
// Visits the line number (previously parsed) AFTER we visited
// the Label.
if (newDebugLineEmitted) {
if (!findLabelsOnly) {
methodVisitor.visitLineNumber(debugEmittedLine,
getLabel(relativeOffset));
}
newDebugLineEmitted = false;
}
// If a Try/Catch block is here, visit it. The TryCatch table has been
// built before on first pass.
if (listTryCatchStructures.containsKey(relativeOffset)) {
ArrayList<TryCatch> listTryCatch = listTryCatchStructures.get(relativeOffset);
for (TryCatch tcs : listTryCatch) {
if (!findLabelsOnly) {
methodVisitor.visitTryCatchBlock(tcs.getStart(),
tcs.getEnd(), tcs.getHandler(), tcs.getType());
}
}
}
// Now reads the bytecode itself.
int fullOpcode = dexFile.ushort();
int shortOpcode = fullOpcode & 0xff;
int highOrderByte = (fullOpcode >> 8) & 0xff;
// Finding a NOP or "special" instructions such as the special instructions
// such as Switch structures and Fill Array Data.
if (shortOpcode == 0x00) {
// NOP or "special" instructions.
switch (highOrderByte) {
case 0x00: // NOP.
if ((!findLabelsOnly) && (!hasReachedPseudoInstructions)) {
// NOPs are ignored when we have reached the Pseudo Instructions area (at the end
// of the methods. This prevents the stacking of NOPs when reading a file that we
// have previously generated.
methodVisitor.visitInsn(shortOpcode);
}
break;
case 0x01: { // Packed-switch structure
// Since the structure was already parsed (because we parse it
// immediately when finding the packed-switch opcode), we skip it.
int packedSwitchSize = dexFile.ushort();
dexFile.seek(dexFile.getPos() + packedSwitchSize * 4 + 4);
hasReachedPseudoInstructions = true;
break;
}
case 0x02: { // Sparse-switch
// Since the structure was already parsed (because we parse it
// immediately when finding the packed-switch opcode), we skip it.
int sparseSwitchSize = dexFile.ushort();
dexFile.seek(dexFile.getPos() + sparseSwitchSize * 4 * 2);
hasReachedPseudoInstructions = true;
break;
}
case 0x03: { // Fill Array Data
// Since the structure was already parsed (because we parse it
// immediately when finding the fill-array-data opcode), we skip it.
int elementWidth = dexFile.ushort();
int elementSize = dexFile.uint();
int totalSizeInBytes = elementWidth * elementSize;
int codeUnitNumber = ((totalSizeInBytes + 1) / 2);
dexFile.seek(dexFile.getPos() + codeUnitNumber * 2);
hasReachedPseudoInstructions = true;
break;
}
default :
throw new RuntimeException("Unknown opcode after a 0x00 : 0x" + Integer.toHexString(highOrderByte) + " at " + Integer.toHexString(dexFile.getPos() - 2));
}
} else {
// Other instructions than NOP are found. We thus consider we are not in a
// "Pseudo Instructions" area, or have left one.
hasReachedPseudoInstructions = false;
switch (shortOpcode) {
case 0x1: // 12x format.
case 0x4:
case 0x7: {
if (findLabelsOnly) {
// InstructionFormat12X.skip(dexFile);
} else {
int regA = InstructionFormat12X.getRegisterA(fullOpcode);
int regB = InstructionFormat12X.getRegisterB(fullOpcode);
visitVarInsn(shortOpcode, methodVisitor, regA, regB);
}
break;
}
case 0x2: // 22x format.
case 0x5:
case 0x8: {
if (findLabelsOnly) {
InstructionFormat22X.skip(dexFile);
} else {
int regA = InstructionFormat22X.getRegisterA(fullOpcode);
int regB = InstructionFormat22X.getRegisterB(dexFile);
visitVarInsn(shortOpcode, methodVisitor, regA, regB);
}
break;
}
case 0x3: // 32x format.
case 0x6:
case 0x9: {
if (findLabelsOnly) {
InstructionFormat32X.skip(dexFile);
} else {
int regA = InstructionFormat32X.getRegisterA(dexFile);
int regB = InstructionFormat32X.getRegisterB(dexFile);
visitVarInsn(shortOpcode, methodVisitor, regA, regB);
}
break;
}
case 0xa: // 11x format.
case 0xb:
case 0xc:
case 0xd:
case 0xf:
case 0x10:
case 0x11:
case 0x1d:
case 0x1e:
case 0x27: {
if (findLabelsOnly) {
// InstructionFormat11X.skip(dexFile);
} else {
int regA = InstructionFormat11X.getRegisterA(fullOpcode);
methodVisitor.visitIntInsn(shortOpcode, regA);
}
break;
}
case 0xe: { // 10x format.
if (!findLabelsOnly) methodVisitor.visitInsn(shortOpcode);
break;
}
case 0x12: { // 11n format.
if (findLabelsOnly) {
// InstructionFormat11N.skip(dexFile);
} else {
int regA = InstructionFormat11N.getRegisterA(fullOpcode);
int literalB = InstructionFormat11N.getLiteralB(fullOpcode);
visitVarInsn(shortOpcode, methodVisitor, regA, literalB);
}
break;
}
case 0x13: // 21s format.
case 0x16: {
if (findLabelsOnly) {
InstructionFormat21S.skip(dexFile);
} else {
int regA = InstructionFormat21S.getRegisterA(fullOpcode);
int literalB = InstructionFormat21S.getLiteralB(dexFile);
visitVarInsn(shortOpcode, methodVisitor, regA, literalB);
}
break;
}
case 0x14: // 31i format.
case 0x17: {
if (findLabelsOnly) {
InstructionFormat31I.skip(dexFile);
} else {
int regA = InstructionFormat31I.getRegisterA(fullOpcode);
int literalB = InstructionFormat31I.getLiteralB(dexFile);
visitVarInsn(shortOpcode, methodVisitor, regA, literalB);
}
break;
}
case 0x15: { // 21h format.
if (findLabelsOnly) {
InstructionFormat21H.skip(dexFile);
} else {
int regA = InstructionFormat21H.getRegisterA(fullOpcode);
int literalB = InstructionFormat21H.getLiteralB(dexFile);
visitVarInsn(shortOpcode, methodVisitor, regA, literalB << 16);
}
break;
}
case 0x19: { // 21h format.
if (findLabelsOnly) {
InstructionFormat21H.skip(dexFile);
} else {
int regA = InstructionFormat21H.getRegisterA(fullOpcode);
int literalB = InstructionFormat21H.getLiteralB(dexFile);
visitVarInsn(shortOpcode, methodVisitor, regA, (long)literalB << 48);
}
break;
}
case 0x18: { // 51l format.
if (findLabelsOnly) {
InstructionFormat51L.skip(dexFile);
} else {
int regA = InstructionFormat51L.getRegisterA(fullOpcode);
long literalB = InstructionFormat51L.getLiteralB(dexFile);
methodVisitor.visitVarInsn(shortOpcode, regA, literalB);
}
break;
}
case 0x1a: { // 21c format.
if (findLabelsOnly) {
InstructionFormat21C.skip(dexFile);
} else {
int regA = InstructionFormat21C.getRegisterA(fullOpcode);
int indexB = InstructionFormat21C.getIndexB(dexFile);
String string = dexFile.getStringItemFromStringIndex(indexB);
methodVisitor.visitStringInsn(shortOpcode, regA, string);
}
break;
}
case 0x1b: { // 31c format.
if (findLabelsOnly) {
InstructionFormat31C.skip(dexFile);
} else {
int regA = InstructionFormat31C.getRegisterA(fullOpcode);
int indexB = InstructionFormat31C.getIndexB(dexFile);
String string = dexFile.getStringItemFromStringIndex(indexB);
methodVisitor.visitStringInsn(shortOpcode, regA, string);
}
break;
}
case 0x1c: { // 21c format.
if (findLabelsOnly) {
InstructionFormat21C.skip(dexFile);
} else {
int regA = InstructionFormat21C.getRegisterA(fullOpcode);
int indexB = InstructionFormat21C.getIndexB(dexFile);
String typeName = dexFile.getStringItemFromTypeIndex(indexB);
methodVisitor.visitTypeInsn(shortOpcode, regA, 0, 0, typeName);
}
break;
}
case 0x1f: { // 21c format.
if (findLabelsOnly) {
InstructionFormat21C.skip(dexFile);
} else {
int regA = InstructionFormat21C.getRegisterA(fullOpcode);
int indexB = InstructionFormat21C.getIndexB(dexFile);
String typeName = dexFile.getStringItemFromTypeIndex(indexB);
methodVisitor.visitTypeInsn(shortOpcode, 0, regA, 0, typeName);
}
break;
}
case 0x20: { // 22c format.
if (findLabelsOnly) {
InstructionFormat22C.skip(dexFile);
} else {
int regA = InstructionFormat22C.getRegisterA(fullOpcode);
int regB = InstructionFormat22C.getRegisterB(fullOpcode);
int indexC = InstructionFormat22C.getIndexC(dexFile);
String typeName = dexFile.getStringItemFromTypeIndex(indexC);
methodVisitor.visitTypeInsn(shortOpcode, regA, regB, 0, typeName);
}
break;
}
case 0x21: { // 12x format.
if (findLabelsOnly) {
// InstructionFormat12X.skip(dexFile);
} else {
int regA = InstructionFormat12X.getRegisterA(fullOpcode);
int regB = InstructionFormat12X.getRegisterB(fullOpcode);
methodVisitor.visitArrayLengthInsn(regA, regB);
}
break;
}
case 0x22: { // 21c format.
if (findLabelsOnly) {
InstructionFormat21C.skip(dexFile);
} else {
int regA = InstructionFormat21C.getRegisterA(fullOpcode);
int indexB = InstructionFormat21C.getIndexB(dexFile);
String typeName = dexFile.getStringItemFromTypeIndex(indexB);
methodVisitor.visitTypeInsn(shortOpcode, regA, 0, 0, typeName);
}
break;
}
case 0x23: { // 22c format.
if (findLabelsOnly) {
InstructionFormat22C.skip(dexFile);
} else {
int registerArray = InstructionFormat22C.getRegisterA(fullOpcode);
int regB = InstructionFormat22C.getRegisterB(fullOpcode);
int indexC = InstructionFormat22C.getIndexC(dexFile);
String typeName = dexFile.getStringItemFromTypeIndex(indexC);
methodVisitor.visitTypeInsn(shortOpcode, registerArray, 0, regB, typeName);
// We have to store the type linked to the register used by the array declaration,
// in case it is filled with a Fill Array Data instruction.
int type = getTypeFromTypeArray(typeName);
registerArrayType.put(registerArray, type);
}
break;
}
case 0x24: { // 35c format.
if (findLabelsOnly) {
InstructionFormat35C.skip(dexFile);
} else {
int index = InstructionFormat35C.getIndex(dexFile);
int[] registers = InstructionFormat35C.getRegisters(dexFile, fullOpcode);
visitMultiANewArrayInsn(methodVisitor, index, registers);
}
break;
}
case 0x25: { // 3rc format.
if (findLabelsOnly) {
InstructionFormat3RC.skip(dexFile);
} else {
int index = InstructionFormat3RC.getIndex(dexFile);
int[] registers = InstructionFormat3RC.getRegisters(dexFile, fullOpcode);
visitMultiANewArrayInsn(methodVisitor, index, registers);
}
break;
}
case 0x26: { // 31t format.
if (findLabelsOnly) {
InstructionFormat31T.skip(dexFile);
} else {
int regA = InstructionFormat31T.getRegisterA(fullOpcode);
int offset = InstructionFormat31T.getOffset(dexFile, fullOpcode);
visitFillArrayData(methodVisitor, regA, offset);
}
break;
}
case 0x28: { // 10t format.
int offset = InstructionFormat10T.getOffset(dexFile, fullOpcode);
visitJumpInsn(shortOpcode, offset, 0, 0, methodVisitor, findLabelsOnly);
break;
}
case 0x29: { // 20t format.
int offset = InstructionFormat20T.getOffset(dexFile, fullOpcode);
visitJumpInsn(shortOpcode, offset, 0, 0, methodVisitor, findLabelsOnly);
break;
}
case 0x2a: { // 30t format.
int offset = InstructionFormat30T.getOffset(dexFile, fullOpcode);
visitJumpInsn(shortOpcode, offset, 0, 0, methodVisitor, findLabelsOnly);
break;
}
case 0x2b: // 31t format.
case 0x2c: {
parseAndVisitSwitchCase(methodVisitor, fullOpcode, findLabelsOnly);
break;
}
case 0x2d: // 23x format.
case 0x2e:
case 0x2f:
case 0x30:
case 0x31: {
if (findLabelsOnly) {
InstructionFormat23X.skip(dexFile);
} else {
int regA = InstructionFormat23X.getRegisterA(fullOpcode);
int regBAndC = InstructionFormat23X.getEncodedRegisterBAndC(dexFile);
int regB = InstructionFormat23X.getRegisterBFromEncodedRegisterBAndC(regBAndC);
int regC = InstructionFormat23X.getRegisterCFromEncodedRegisterBAndC(regBAndC);
methodVisitor.visitOperationInsn(shortOpcode, regA, regB, regC, 0);
}
break;
}
case 0x32: // 22t format.
case 0x33:
case 0x34:
case 0x35:
case 0x36:
case 0x37: {
int regA = InstructionFormat22T.getRegisterA(fullOpcode);
int regB = InstructionFormat22T.getRegisterB(fullOpcode);
int offset = InstructionFormat22T.getOffset(dexFile, fullOpcode);
visitJumpInsn(shortOpcode, offset, regA, regB, methodVisitor, findLabelsOnly);
break;
}
case 0x38: // 21t format.
case 0x39:
case 0x3a:
case 0x3b:
case 0x3c:
case 0x3d: {
int regA = InstructionFormat21T.getRegisterA(fullOpcode);
int offset = InstructionFormat21T.getOffset(dexFile, fullOpcode);
visitJumpInsn(shortOpcode, offset, regA, 0, methodVisitor, findLabelsOnly);
break;
}
case 0x44: // 23x format (Array Operations).
case 0x45:
case 0x46:
case 0x47:
case 0x48:
case 0x49:
case 0x4a:
case 0x4b:
case 0x4c:
case 0x4d:
case 0x4e:
case 0x4f:
case 0x50:
case 0x51: {
if (findLabelsOnly) {
InstructionFormat23X.skip(dexFile);
} else {
int regA = InstructionFormat23X.getRegisterA(fullOpcode);
int regBAndC = InstructionFormat23X.getEncodedRegisterBAndC(dexFile);
int regB = InstructionFormat23X.getRegisterBFromEncodedRegisterBAndC(regBAndC);
int regC = InstructionFormat23X.getRegisterCFromEncodedRegisterBAndC(regBAndC);
methodVisitor.visitArrayOperationInsn(shortOpcode, regA, regB, regC);
}
break;
}
case 0x52: // 22c format.
case 0x53:
case 0x54:
case 0x55:
case 0x56:
case 0x57:
case 0x58:
case 0x59:
case 0x5a:
case 0x5b:
case 0x5c:
case 0x5d:
case 0x5e:
case 0x5f: {
if (findLabelsOnly) {
InstructionFormat22C.skip(dexFile);
} else {
int regA = InstructionFormat22C.getRegisterA(fullOpcode);
int regB = InstructionFormat22C.getRegisterB(fullOpcode);
int indexC = InstructionFormat22C.getIndexC(dexFile);
visitFieldInsn(methodVisitor, shortOpcode, regA, regB, indexC);
}
break;
}
case 0x60: // 21c format.
case 0x61:
case 0x62:
case 0x63:
case 0x64:
case 0x65:
case 0x66:
case 0x67:
case 0x68:
case 0x69:
case 0x6a:
case 0x6b:
case 0x6c:
case 0x6d: {
if (findLabelsOnly) {
InstructionFormat21C.skip(dexFile);
} else {
int regA = InstructionFormat21C.getRegisterA(fullOpcode);
int indexC = InstructionFormat21C.getIndexB(dexFile);
visitFieldInsn(methodVisitor, shortOpcode, regA, 0, indexC);
}
break;
}
case 0x6e:
case 0x6f:
case 0x70:
case 0x71:
case 0x72: {
if (findLabelsOnly) {
InstructionFormat35C.skip(dexFile);
} else {
int index = InstructionFormat35C.getIndex(dexFile);
int[] registers = InstructionFormat35C.getRegisters(dexFile, fullOpcode);
visitMethodInstruction(methodVisitor, shortOpcode, index, registers);
}
break;
}
case 0x74:
case 0x75:
case 0x76:
case 0x77:
case 0x78: {
if (findLabelsOnly) {
InstructionFormat3RC.skip(dexFile);
} else {
int index = InstructionFormat3RC.getIndex(dexFile);
int[] registers = InstructionFormat3RC.getRegisters(dexFile, fullOpcode);
visitMethodInstruction(methodVisitor, shortOpcode, index, registers);
}
break;
}
case 0x7b:
case 0x7c:
case 0x7d:
case 0x7e:
case 0x7f:
case 0x80:
case 0x81:
case 0x82:
case 0x83:
case 0x84:
case 0x85:
case 0x86:
case 0x87:
case 0x88:
case 0x89:
case 0x8a:
case 0x8b:
case 0x8c:
case 0x8d:
case 0x8e:
case 0x8f: {
if (findLabelsOnly) {
// InstructionFormat12X.skip(dexFile);
} else {
int regA = InstructionFormat12X.getRegisterA(fullOpcode);
int regB = InstructionFormat12X.getRegisterB(fullOpcode);
methodVisitor.visitOperationInsn(shortOpcode, regA, regB, 0, 0);
}
break;
}
case 0x90:
case 0x91:
case 0x92:
case 0x93:
case 0x94:
case 0x95:
case 0x96:
case 0x97:
case 0x98:
case 0x99:
case 0x9a:
case 0x9b:
case 0x9c:
case 0x9d:
case 0x9e:
case 0x9f:
case 0xa0:
case 0xa1:
case 0xa2:
case 0xa3:
case 0xa4:
case 0xa5:
case 0xa6:
case 0xa7:
case 0xa8:
case 0xa9:
case 0xaa:
case 0xab:
case 0xac:
case 0xad:
case 0xae:
case 0xaf: {
if (findLabelsOnly) {
InstructionFormat23X.skip(dexFile);
} else {
int regA = InstructionFormat23X.getRegisterA(fullOpcode);
int regBAndC = InstructionFormat23X.getEncodedRegisterBAndC(dexFile);
int regB = InstructionFormat23X.getRegisterBFromEncodedRegisterBAndC(regBAndC);
int regC = InstructionFormat23X.getRegisterCFromEncodedRegisterBAndC(regBAndC);
methodVisitor.visitOperationInsn(shortOpcode, regA, regB, regC, 0);
}
break;
}
case 0xb0:
case 0xb1:
case 0xb2:
case 0xb3:
case 0xb4:
case 0xb5:
case 0xb6:
case 0xb7:
case 0xb8:
case 0xb9:
case 0xba:
case 0xbb:
case 0xbc:
case 0xbd:
case 0xbe:
case 0xbf:
case 0xc0:
case 0xc1:
case 0xc2:
case 0xc3:
case 0xc4:
case 0xc5:
case 0xc6:
case 0xc7:
case 0xc8:
case 0xc9:
case 0xca:
case 0xcb:
case 0xcc:
case 0xcd:
case 0xce:
case 0xcf: {
if (findLabelsOnly) {
// InstructionFormat12X.skip(dexFile);
} else {
int regA = InstructionFormat12X.getRegisterA(fullOpcode);
int regB = InstructionFormat12X.getRegisterB(fullOpcode);
methodVisitor.visitOperationInsn(shortOpcode, regA, regA, regB, 0);
}
break;
}
case 0xd0: // 22s format.
case 0xd1:
case 0xd2:
case 0xd3:
case 0xd4:
case 0xd5:
case 0xd6:
case 0xd7: {
if (findLabelsOnly) {
InstructionFormat22S.skip(dexFile);
} else {
int regA = InstructionFormat22S.getRegisterA(fullOpcode);
int regB = InstructionFormat22S.getRegisterB(fullOpcode);
int literalC = InstructionFormat22S.getLiteralC(dexFile);
methodVisitor.visitOperationInsn(shortOpcode, regA, regB, 0, literalC);
}
break;
}
case 0xd8: // 22b format.
case 0xd9:
case 0xda:
case 0xdb:
case 0xdc:
case 0xdd:
case 0xde:
case 0xdf:
case 0xe0:
case 0xe1:
case 0xe2: {
if (findLabelsOnly) {
InstructionFormat22B.skip(dexFile);
} else {
int regA = InstructionFormat22B.getRegisterA(fullOpcode);
int regBAndLitC = InstructionFormat22B.getEncodedRegisterBAndLiteralC(dexFile);
int regB = InstructionFormat22B.getRegisterBFromEncodedRegisterBAndC(regBAndLitC);
int literalC = InstructionFormat22B.getLiteralCFromEncodedRegisterBAndLiteralC(regBAndLitC);
methodVisitor.visitOperationInsn(shortOpcode, regA, regB, 0, literalC);
}
break;
}
default:
// All other opcodes are considered unused.
if (!findLabelsOnly) methodVisitor.visitInsn(shortOpcode);
break;
}
}
}
}
/**
* Converts an array descriptor to a value (such as Opcodes.VALUE_INT). Only primitive types
* can be converted, other types such as reference will return -1. This is like that because
* only primitive can be used for the Fill Array Data instruction.
* @param typeName the type of the array. Should be in the form "[X]", where X is a primitive
* descriptor.
* @return the value of elements contained in the array (such as {@link Opcodes#VALUE_INT}), or -1.
*/
private static int getTypeFromTypeArray(String typeName) {
int result = -1;
if ((typeName.length() == 2) && (typeName.startsWith("["))) {
char t = typeName.charAt(1);
switch (t) {
case 'Z' :
result = Opcodes.VALUE_BOOLEAN;
break;
case 'B' :
result = Opcodes.VALUE_BYTE;
break;
case 'S' :
result = Opcodes.VALUE_SHORT;
break;
case 'C' :
result = Opcodes.VALUE_CHAR;
break;
case 'I' :
result = Opcodes.VALUE_INT;
break;
case 'J' :
result = Opcodes.VALUE_LONG;
break;
case 'F' :
result = Opcodes.VALUE_FLOAT;
break;
case 'D' :
result = Opcodes.VALUE_DOUBLE;
break;
}
}
return result;
}
/**
* Parses the debug_info_item information of the given debug opcode. We stop the
* reading when the debugAddress doesn't coincide with the bytecode pointer.
* Updates the debugAddress and debugCurrentOpcodeOffset. However, the visit
* isn't done here, but by the calling method.
* The dex file reader position <i>is</i> saved.
* @param methodVisitor the visitor to visit the debug information item.
* @param currentBytecodeOffset Offset <i>from the method opcodes</i>
* currently being parsed.
* @param findLabelsOnly true if no visitor must be called, because we only want to set up
* the Labels by parsing this structure.
*/
protected void parseDebugInformationItem(MethodVisitor methodVisitor,
int currentBytecodeOffset, boolean findLabelsOnly) {
int saveReaderPosition = dexFile.getPos();
dexFile.seek(debugCurrentOpcodeOffset);
newDebugLineEmitted = false;
int nextDebugAddress;
boolean debugParsingContinue = true;
while (debugParsingContinue) {
int debugOpcode = dexFile.ubyte();
switch (debugOpcode) {
case Opcodes.DBG_END_SEQUENCE:
debugParsingContinue = false;
break;
case Opcodes.DBG_ADVANCE_PC:
debugAddress += dexFile.uleb128();
break;
case Opcodes.DBG_ADVANCE_LINE:
debugLine += dexFile.sleb128();
break;
case Opcodes.DBG_START_LOCAL: {
// Makes sure the variable is read at the right address.
if ((debugAddress * 2) != currentBytecodeOffset) {
debugParsingContinue = false;
} else {
parseDebugStartLocalVariable(methodVisitor, currentBytecodeOffset, false,
findLabelsOnly);
}
break;
}
case Opcodes.DBG_START_LOCAL_EXTENDED: {
// Makes sure the variable is read at the right address.
if ((debugAddress * 2) != currentBytecodeOffset) {
debugParsingContinue = false;
} else {
parseDebugStartLocalVariable(methodVisitor, currentBytecodeOffset, true,
findLabelsOnly);
}
break;
}
case Opcodes.DBG_END_LOCAL: {
// Makes sure the variable is read at the right address.
if ((debugAddress * 2) != currentBytecodeOffset) {
debugParsingContinue = false;
} else {
// Adds the END_LOCAL information to the local variable structure. It should
// exist !
int registerNumber = dexFile.uleb128();
if (findLabelsOnly) {
Label endLabel = createAndAddLabel(debugAddress * 2);
addEndLabelToLocalVariableLabels(registerNumber, endLabel);
}
}
break;
}
case Opcodes.DBG_RESTART_LOCAL: {
// Makes sure the variable is read at the right address.
if ((debugAddress * 2) != currentBytecodeOffset) {
debugParsingContinue = false;
} else {
// Adds the RESTART_LOCAL information to the local variable structure. It
// should exist !
int registerNumber = dexFile.uleb128();
if (findLabelsOnly) {
Label restartLabel = createAndAddLabel(debugAddress * 2);
addRestartLabelToLocalVariableLabels(registerNumber, restartLabel);
}
}
break;
}
case Opcodes.DBG_SET_PROLOGUE_END:
debugPrologueEnd = true;
break;
case Opcodes.DBG_SET_EPILOGUE_BEGIN:
debugEpilogueBegin = true;
break;
case Opcodes.DBG_SET_FILE: {
int nameIndex = dexFile.uleb128_p1();
if (nameIndex != Opcodes.NO_INDEX_SIGNED) {
debugSourceFile = dexFile.getStringItemFromStringIndex(nameIndex);
}
break;
}
default:
// Special Opcode (>=0xa).
// We only emit a New Debug Line if the address it points to is the current one.
// Else, we do NOT make the reader advance to the next opcode, so that we
// check, next time, if the line number we were on is now become
// the current one.
int adjustedOpcode = debugOpcode - Opcodes.DBG_FIRST_SPECIAL;
nextDebugAddress = debugAddress + adjustedOpcode / Opcodes.DBG_LINE_RANGE;
// Note : the debugAddress must be multiplied by 2 when comparing,
// because it is an offset of 16-bit units.
if (currentBytecodeOffset != (nextDebugAddress * 2)) {
debugParsingContinue = false;
} else {
debugEpilogueBegin = false;
debugPrologueEnd = false;
debugLine += Opcodes.DBG_LINE_BASE + (adjustedOpcode % Opcodes.DBG_LINE_RANGE);
// We store this new emitted debug line so that a Label found can be linked
// to this line.
newDebugLineEmitted = true;
debugEmittedLine = debugLine;
debugAddress = nextDebugAddress;
// By default, if we find a line number, we add a Label, because in
// ASM line numbers always refer to a Label.
createAndAddLabel(debugAddress * 2); // * 2 because offset of 16-bit units.
}
break;
}
// If the parsing stops, we don't move the reader after the debug instruction
// that made it stops. We will parse the same instruction on the next iteration.
if (debugParsingContinue) {
debugCurrentOpcodeOffset = dexFile.getPos();
}
}
dexFile.seek(saveReaderPosition);
}
/**
* Parses a DBG_START_LOCAL or DBG_START_LOCAL_EXTENDED instruction, and visits
* the local variables.
* @param methodVisitor the visitor to visit the debug information item.
* @param currentBytecodeOffset Offset <i>from the method opcodes</i> currently being parsed.
* @param isLocalExtended true if the instruction concerns a LOCAL_EXTENDED variable.
* @param findLabelsOnly true if no visitor must be called, because we only want to set up
* the Labels by parsing this structure.
*/
protected void parseDebugStartLocalVariable(MethodVisitor methodVisitor,
int currentBytecodeOffset, boolean isLocalExtended, boolean findLabelsOnly) {
int registerNumber = dexFile.uleb128();
int variableNameIndex = dexFile.uleb128_p1();
int variableTypeIndex = dexFile.uleb128_p1();
int variableSignatureIndex = 0;
if (isLocalExtended) {
variableSignatureIndex = dexFile.uleb128_p1();
}
String variableName = (variableNameIndex == Opcodes.NO_INDEX_SIGNED) ?
null : dexFile.getStringItemFromStringIndex(variableNameIndex);
String variableType = (variableTypeIndex == Opcodes.NO_INDEX_SIGNED) ?
null : dexFile.getStringItemFromTypeIndex(variableTypeIndex);
String variableSignature = isLocalExtended ?
((variableSignatureIndex == Opcodes.NO_INDEX_SIGNED) ?
null : dexFile.getStringItemFromStringIndex(variableSignatureIndex)):
null;
Label startLabel = createAndAddLabel(currentBytecodeOffset);
// Creates the localVariableLabels structure for this variable, if first pass.
// We always create a new Local Variable and adds it to the list. Some variable can share the
// same register, but of different type, so they must be managed separately.
if (findLabelsOnly) {
LocalVariable localVariable = new LocalVariable(registerNumber, variableName,
variableType, variableSignature, startLabel, null, null);
addNewLocalVariable(registerNumber, localVariable);
}
}
/**
* Returns the LAST Local Variable used according to the given register number.
* If it doesn't exist, it creates it.
* @param registerNumber
* @return a Local Variable, new or existing.
*/
protected LocalVariable getLocalVariable(int registerNumber) {
LocalVariable localVariable;
if (localVariableLists.containsKey(registerNumber)) {
List<LocalVariable> list = localVariableLists.get(registerNumber);
localVariable = list.get(list.size() - 1);
} else {
List<LocalVariable> list = new ArrayList<LocalVariable>(1);
localVariable = new LocalVariable(registerNumber);
list.add(localVariable);
localVariableLists.put(registerNumber, list);
}
return localVariable;
}
/**
* Adds the Local Variable at the end of the list designed by the register number.
* @param registerNumber the register number of the Local Variable.
* @param localVariable the Local Variable to add.
*/
protected void addNewLocalVariable(int registerNumber, LocalVariable localVariable) {
if (localVariableLists.containsKey(registerNumber)) {
List<LocalVariable> list = localVariableLists.get(registerNumber);
list.add(localVariable);
} else {
List<LocalVariable> list = new ArrayList<LocalVariable>(1);
list.add(localVariable);
localVariableLists.put(registerNumber, list);
}
}
/**
* Parses the "tries" structure. It may contains several "try_item" items.
* In order to get all the information needed, it will also parse the
* encoded_catch_handler_list (only reaching the information needed).
* The dex file reader position <i>is</i> modified.
* @param tryItemOffset offset to the "tries" structure (which offset is given in
* the code_item structure), from the beginning of the file.
* @param triesSize number of "try" structure.
*/
protected void parseTryItemsFormat(int tryItemOffset, int triesSize) {
dexFile.seek(tryItemOffset);
for (int tryItemIndex = 0; tryItemIndex < triesSize ; tryItemIndex++) {
int startAddress = dexFile.uint() * 2; // * 2 because encoded in 16-bit units.
int instructionCount = dexFile.ushort() * 2;
int handlerOffset = dexFile.ushort();
// Seek the handler information into the catch_handler_item located in the
// catch_handler_list structure. The offset inside this structure is given
// by the field handlerOffset.
int saveReaderPositionInTryItem = dexFile.getPos();
dexFile.seek(tryItemOffset + triesSize * TRY_ITEM_STRUCTURE_SIZE
+ handlerOffset);
int nbCatchTypesRead = dexFile.sleb128();
boolean isCatchAll = (nbCatchTypesRead <= 0);
int nbCatchTypes = Math.abs(nbCatchTypesRead);
// Read the encoded_type_addr_pair list.
// For each exception handled type, we create a TryCatch item that will
// be visited on the second pass.
for (int typeAddrPairIndex = 0; typeAddrPairIndex < nbCatchTypes; typeAddrPairIndex++) {
int typeIndex = dexFile.uleb128();
int addr = dexFile.uleb128() * 2; // * 2 because mesured in 16-bit units.
// Creates the Labels.
Label startLabel = createAndAddLabel(startAddress);
Label endLabel = createAndAddLabel(startAddress + instructionCount);
Label handlerLabel = createAndAddLabel(addr);
TryCatch tcs = new TryCatch(startLabel, endLabel, handlerLabel,
dexFile.getStringItemFromTypeIndex(typeIndex));
// Adds this TryCatch structure to the map, so that we can call the
// visitTryCatchBlock when visiting the bytecode itself by comparing
// the current offset to the offsets of the tryCatchStructures, and
// see if one (or more) fits.
addTryCatchStructure(tcs, startAddress);
}
// Manage Catch All, if any.
if (isCatchAll) {
int catchAllAddress = dexFile.uleb128() * 2; // * 2 because mesured in 16-bit units.
Label startLabel = createAndAddLabel(startAddress);
Label endLabel = createAndAddLabel(startAddress + instructionCount);
Label handlerLabel = createAndAddLabel(catchAllAddress);
TryCatch tcs = new TryCatch(startLabel, endLabel, handlerLabel, null); // In ASM, type is Null for catch all.
addTryCatchStructure(tcs, startAddress);
}
dexFile.seek(saveReaderPositionInTryItem);
}
}
/**
* Adds a new Label to the Label list, mapped according to its offset from the first
* byte of the code of the method. Doesn't add it if it already exists.
* @param offset offset from the first byte of the code of the method.
*/
protected void addLabel(int offset) {
if (!labels.containsKey(offset)) {
Label label = new Label();
label.setOffset(offset);
labels.put(offset, label);
}
}
/**
* Convenient method to add an (already resolved) label to the Label list. Returns
* a reference to the actually labels here. This is useful is a label already exists
* here.
* @param label label to add.
* @return the label actually put. This is useful not to have two labels at the same
* location.
*/
protected Label addLabel(Label label) {
int offset = label.getOffset();
Label returnedLabel = label;
if (labels.containsKey(offset)) {
returnedLabel = labels.get(offset);
} else {
labels.put(offset, label);
}
return returnedLabel;
}
/**
* Convenient method to add (already resolved) labels to the Label list.
* @param labelList list of the Labels to add.
*/
protected void addLabels(Label[] labelList) {
for (Label label : labelList) {
addLabel(label);
}
}
/**
* Convenient method to add a TryCatchStructure to the listTryCatchStructures. As it
* contains lists of TryCatchStructure, we have to make sure that a list already
* exists before adding the structure, else we have to create one.
* @param tcs TryCatchStructure to add.
* @param relativeOffset Offset to where the Try is in the bytecode, relative
* to the first byte of the code of the method.
*/
protected void addTryCatchStructure(TryCatch tcs, int relativeOffset) {
ArrayList<TryCatch> list = listTryCatchStructures.get(relativeOffset);
if (list != null) {
list.add(tcs);
} else {
list = new ArrayList<TryCatch>(1);
list.add(tcs);
listTryCatchStructures.put(relativeOffset, list);
}
}
/**
* Convenient method to get the Label related to the offset (relative to the first
* byte of the code of the method).
* @param offset offset of the Label, relative to the first byte of the code of the method.
* @return the Label related to the offset given, or Null if the Label isn't found.
*/
protected Label getLabel(int offset) {
return labels.get(offset);
}
/**
* Creates and adds to the Labels list a new Label which offset (relative to the first
* byte of the code of the method) is given, but if a Label already exists here,
* returns this existing label.
* @param offset offset of the Label, relative to the first byte of the code of
* the method).
* @return A new Label, or the Label existing at this offset.
*/
protected Label createAndAddLabel(int offset) {
Label returnedLabel;
if (labels.containsKey(offset)) {
returnedLabel = labels.get(offset);
} else {
returnedLabel = new Label();
returnedLabel.setOffset(offset);
labels.put(offset, returnedLabel);
}
return returnedLabel;
}
/**
* Visits a Var Instruction.
* @param opcodeByte 8-bit opcode of the instruction.
* @param methodVisitor visitor to call.
* @param destinationRegister the destination register.
* @param var the operand of the instruction to be visited. This operand is
* either a value or a source Register.
*/
protected void visitVarInsn(int opcodeByte, MethodVisitor methodVisitor,
int destinationRegister, int var) {
methodVisitor.visitVarInsn(opcodeByte, destinationRegister, var);
}
/**
* Visits a Var Instruction.
* @param opcodeByte 8-bit opcode of the instruction.
* @param methodVisitor visitor to call.
* @param destinationRegister the destination register.
* @param var the operand of the instruction to be visited. This operand is
* either a value or a source Register.
*/
protected void visitVarInsn(int opcodeByte, MethodVisitor methodVisitor,
int destinationRegister, long var) {
methodVisitor.visitVarInsn(opcodeByte, destinationRegister, var);
}
/**
* Method used by the methods that decodes the invoke-kind, in order to get the
* invoked method information and call the visitor.
* The dex file reader position <i>is</i> saved.
* @param methodVisitor visitor to call.
* @param opcodeByte 8-bit opcode of the instruction.
* @param methodIndex method index of the invoked method.
* @param registers list of the registers encoded to pass to the invoked method.
*/
protected void visitMethodInstruction(MethodVisitor methodVisitor, int opcodeByte,
int methodIndex, int[] registers) {
int savePosReader = dexFile.getPos();
dexFile.seek(dexFile.getOffsetMethodIdItem(methodIndex));
int classIndex = dexFile.ushort(); // Get name from class_idx.
int protoIndex = dexFile.ushort();
int nameIndex = dexFile.uint();
String methodOwner = dexFile.getStringItemFromTypeIndex(classIndex);
String methodDescriptor = dexFile.getDescriptorFromPrototypeIndex(protoIndex);
String methodName = dexFile.getStringItemFromStringIndex(nameIndex);
methodVisitor.visitMethodInsn(opcodeByte, methodOwner, methodName, methodDescriptor, registers);
dexFile.seek(savePosReader);
}
/**
* Parses and visits a Jump Instruction (format 10X, 20X, 30X).
* @param opcode the 8-bit opcode of the Instruction.
* @param offset offset relative to the beginning of the Dex file, to go to.
* @param firstRegister first register to test, if any.
* @param secondRegister second register to test, if any.
* @param methodVisitor MethodVisitor to visit the Jump.
* @param findLabelsOnly indicates if the parsing is only about getting the Label offsets.
* In this case, no method from the visitor must be called.
*/
protected void visitJumpInsn(int opcode, int offset, int firstRegister,
int secondRegister, MethodVisitor methodVisitor, boolean findLabelsOnly) {
int relativeOffset = offset - instructionsStartOffset;
if (findLabelsOnly) {
// Generate a Label, which offset is related to the current opcode.
addLabel(relativeOffset);
} else {
methodVisitor.visitJumpInsn(opcode, getLabel(relativeOffset), firstRegister, secondRegister);
}
}
/**
* Visits a given Field Instruction, and parses it.
* The dex file reader position <i>is</i> saved.
* @param methodVisitor MethodVisitor to visit the instruction.
* @param opcode Opcode of the instruction
* @param registerA First register
* @param registerB Second register
* @param index index of the field definition
*/
protected void visitFieldInsn(MethodVisitor methodVisitor, int opcode, int registerA, int registerB, int index) {
int savePosReader = dexFile.getPos();
dexFile.seek(dexFile.getOffsetFieldIdItem(index));
int classIndex = dexFile.ushort(); // Get name from class_idx.
int typeIndex = dexFile.ushort();
int nameIndex = dexFile.uint();
String fieldOwner = dexFile.getStringItemFromTypeIndex(classIndex);
String fieldDescriptor = dexFile.getStringItemFromTypeIndex(typeIndex);
String fieldName = dexFile.getStringItemFromStringIndex(nameIndex);
methodVisitor.visitFieldInsn(opcode, fieldOwner, fieldName, fieldDescriptor, registerA, registerB);
dexFile.seek(savePosReader);
}
/**
* Visits a MultiANewArrayInsn Instruction.
* The dex file reader position <i>is</i> saved.
* @param methodVisitor MethodVisitor to visit the instruction.
* @param index the index of the descriptor of the Array.
* @param registers the registers containing the values of the Array.
*/
protected void visitMultiANewArrayInsn(MethodVisitor methodVisitor, int index, int[] registers) {
String desc = dexFile.getStringItemFromTypeIndex(index);
methodVisitor.visitMultiANewArrayInsn(desc, registers);
}
/**
* Visits a FillArrayData instruction. Just like switch/case instruction, we directly
* get to the array data (encoded later) in order to parse them for this visitor.
* @param methodVisitor MethodVisitor to visit the instruction.
* @param register register holding the array.
* @param offset offset of the data structure of the Array.
*/
protected void visitFillArrayData(MethodVisitor methodVisitor, int register, int offset) {
int saveReaderPosition = dexFile.getPos();
// Gets the type of the element held by the register. It should have been read before.
int type = -1;
if (registerArrayType.containsKey(register)) {
type = registerArrayType.get(register);
}
// Gets to the Array Data and parse it.
dexFile.seek(offset);
dexFile.skipShort(); // Skips the 0x300 opcode.
if (type == -1 ) {
switch(dexFile.sshort()) {
case 1:
type = Opcodes.VALUE_BYTE;
break;
case 2:
type = Opcodes.VALUE_SHORT;
break;
case 4:
type = Opcodes.VALUE_INT;
break;
case 8:
type = Opcodes.VALUE_LONG;
break;
default:
}
} else {
dexFile.skipShort(); // Skips Element Width.
}
int elementSize = dexFile.uint();
// Fills the array according to its type.
Object[] elements;
switch (type) {
case Opcodes.VALUE_BYTE: {
elements = new Byte[elementSize];
for (int i = 0; i < elementSize; i++) {
elements[i] = Byte.valueOf(dexFile.sbyte());
}
break;
}
case Opcodes.VALUE_SHORT: {
elements = new Short[elementSize];
for (int i = 0; i < elementSize; i++) {
elements[i] = Short.valueOf(dexFile.sshort());
}
break;
}
case Opcodes.VALUE_CHAR: {
elements = new Character[elementSize];
for (int i = 0; i < elementSize; i++) {
elements[i] = Character.valueOf((char)dexFile.sshort());
}
break;
}
case Opcodes.VALUE_INT: {
elements = new Integer[elementSize];
for (int i = 0; i < elementSize; i++) {
elements[i] = Integer.valueOf(dexFile.sint());
}
break;
}
case Opcodes.VALUE_FLOAT: {
elements = new Float[elementSize];
for (int i = 0; i < elementSize; i++) {
elements[i] = Float.valueOf(Float.intBitsToFloat((int)dexFile.sizedLong(4 - 1)));
}
break;
}
case Opcodes.VALUE_DOUBLE: {
elements = new Double[elementSize];
for (int i = 0; i < elementSize; i++) {
elements[i] = Double.valueOf(Double.longBitsToDouble(dexFile.sizedLong(8 - 1)));
}
break;
}
case Opcodes.VALUE_LONG: {
elements = new Long[elementSize];
for (int i = 0; i < elementSize; i++) {
elements[i] = Long.valueOf(dexFile.sizedLong(8 - 1));
}
break;
}
case Opcodes.VALUE_BOOLEAN: {
elements = new Boolean[elementSize];
for (int i = 0; i < elementSize; i++) {
elements[i] = Boolean.valueOf(dexFile.sbyte() != 0);
}
break;
}
default :
throw new IllegalArgumentException("This type (" + type + ") can't be encoded in an Array of primitive.");
}
methodVisitor.visitFillArrayDataInsn(register, elements);
dexFile.seek(saveReaderPosition);
}
/**
* Parses and visits a Switch Case instruction, whether it be a Packed or Sparse switch.
* Its nature is found according to the given opcode.
* The dex file reader must point after the 16-bit opcode. On return, points after
* the full instruction.
* @param methodVisitor MethodVisitor to visit the switch.
* @param fullOpcode the 16-bit opcode.
* @param findLabelsOnly indicates if the parsing is only about getting the Label offsets.
* In this case, no method from the visitor must be called.
*/
protected void parseAndVisitSwitchCase(MethodVisitor methodVisitor,
int fullOpcode, boolean findLabelsOnly) {
boolean isPackedSwitch = ((fullOpcode & 0xff) == 0x2b);
// Get the offset of the switch (just after the 16-bit opcode).
int switchOffset = dexFile.getPos() - 2;
int regA = InstructionFormat31T.getRegisterA(fullOpcode);
int structureOffset = InstructionFormat31T.getOffset(dexFile, fullOpcode);
if (findLabelsOnly) {
int relativeStructureOffset = structureOffset - instructionsStartOffset;
// Generates a Label to the structure, which offset is related
// to the current opcode.
addLabel(relativeStructureOffset);
// Gets the Labels of the structure, and adds the structure for visiting it
// later.
int relativeSwitchOffset = switchOffset - instructionsStartOffset;
ISwitchCase cs = (isPackedSwitch ?
parsePackedSwitchFormat(relativeSwitchOffset, structureOffset) :
parseSparseSwitchFormat(relativeSwitchOffset, structureOffset));
Label[] switchLabels = cs.getSwitchLabels();
addLabels(switchLabels);
addLabel(cs.getDefaultLabel());
if (isPackedSwitch) {
packedSwitchStructures.put(switchOffset, (PackedSwitch)cs);
} else {
sparseSwitchStructures.put(switchOffset, (SparseSwitch)cs);
}
} else {
// Visits the switch structure.
// The structure SHOULD be here, as it has been put in the first pass.
if (isPackedSwitch) {
PackedSwitch pss = packedSwitchStructures.get(switchOffset);
methodVisitor.visitTableSwitchInsn(regA, pss.getValueMin(), pss.getValueMax(),
pss.getDefaultLabel(), pss.getSwitchLabels());
} else {
SparseSwitch sss = sparseSwitchStructures.get(switchOffset);
methodVisitor.visitLookupSwitchInsn(regA, sss.getDefaultLabel(),
sss.getKeys(), sss.getSwitchLabels());
}
}
}
/**
* Reads a packed-switch structure, from the switch offset and the packed switch
* offset and returns a structure containing all the information read.
* The dex file reader position <i>is</i> saved.
* @param relativeSwitchOffset offset of the first byte of the switch opcode, from
* the beginning of the code of the method.
* @param packedSwitchOffset offset of the packed switch structure, from the
* beginning of the Dex file.
* @return a PackedSwitchStructure containing all the information of the packed
* switch structure.
*/
protected PackedSwitch parsePackedSwitchFormat(int relativeSwitchOffset, int packedSwitchOffset) {
int savedReaderPosition = dexFile.getPos();
PackedSwitch pss = new PackedSwitch();
dexFile.seek(packedSwitchOffset);
dexFile.skipShort(); // Skips identifying pseudo-code (0x100).
int nbEntries = dexFile.ushort();
int valueMin = dexFile.sint();
// Reads the Label list.
Label[] switchLabels = new Label[nbEntries];
for (int entryIndex = 0; entryIndex < nbEntries; entryIndex++) {
// * 2 because branch offsets are word based.
int labelOffset = dexFile.sint() * 2 + relativeSwitchOffset;
Label label = createAndAddLabel(labelOffset);
switchLabels[entryIndex] = label;
}
pss.setSwitchLabels(switchLabels);
// Default label is what follows the switch opcode.
pss.setDefaultLabel(createAndAddLabel(relativeSwitchOffset + 6));
pss.setValueMin(valueMin);
pss.setValueMax(valueMin + nbEntries - 1);
dexFile.seek(savedReaderPosition);
return pss;
}
/**
* Reads a sparse-switch structure, from the switch offset and the packed switch
* offset and returns a structure containing all the information read.
* The dex file reader position <i>is</i> saved.
* @param relativeSwitchOffset offset of the first byte of the switch opcode, from
* the beginning of the code of the method.
* @param sparseSwitchOffset offset of the packed switch structure, from the
* beginning of the Dex file.
* @return a SparseSwitchStructure containing all the information of the sparse
* switch structure.
*/
protected SparseSwitch parseSparseSwitchFormat(int relativeSwitchOffset, int sparseSwitchOffset) {
int savedReaderPosition = dexFile.getPos();
SparseSwitch sss = new SparseSwitch();
dexFile.seek(sparseSwitchOffset);
dexFile.skipShort(); // Skips identifying pseudo-code (0x200).
int nbEntries = dexFile.ushort();
// Reads the Keys array.
int[] keys = new int[nbEntries];
for (int entryIndex = 0; entryIndex < nbEntries; entryIndex++) {
keys[entryIndex] = dexFile.sint();
}
// Reads the Targets array.
Label[] switchLabels = new Label[nbEntries];
for (int entryIndex = 0; entryIndex < nbEntries; entryIndex++) {
// * 2 because branch offsets are word based.
int labelOffset = dexFile.sint() * 2 + relativeSwitchOffset;
Label label = createAndAddLabel(labelOffset);
switchLabels[entryIndex] = label;
}
sss.setSwitchLabels(switchLabels);
sss.setKeys(keys);
// Default label is what follows the switch opcode.
sss.setDefaultLabel(createAndAddLabel(relativeSwitchOffset + 6));
dexFile.seek(savedReaderPosition);
return sss;
}
/**
* Adds an End Label to a Local Variable labels structure.
* @param registerNumber register number.
* @param endLabel the Label to add.
*/
protected void addEndLabelToLocalVariableLabels(int registerNumber, Label endLabel) {
LocalVariable localVariable = getLocalVariable(registerNumber);
localVariable.addEnd(endLabel);
}
/**
* Adds an Restart Label to a Local Variable labels structure.
* @param registerNumber register number.
* @param restartLabel the Label to add.
*/
protected void addRestartLabelToLocalVariableLabels(int registerNumber, Label restartLabel) {
LocalVariable localVariable = getLocalVariable(registerNumber);
localVariable.addRestart(restartLabel);
}
}