/*
* Copyright (c) 2009-2012 Panxiaobo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.googlecode.dex2jar.reader;
import com.googlecode.dex2jar.DexException;
import com.googlecode.dex2jar.DexLabel;
import com.googlecode.dex2jar.DexOpcodes;
import com.googlecode.dex2jar.Method;
import com.googlecode.dex2jar.reader.DexDebugInfoReader.LocalVariable;
import com.googlecode.dex2jar.reader.io.DataIn;
import com.googlecode.dex2jar.visitors.DexCodeVisitor;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
/**
* 用于读取方法的指令
*
* @author <a href="mailto:pxb1988@gmail.com">Panxiaobo</a>
* @version $Rev$
*/
/* default */class DexCodeReader implements DexOpcodes {
/**
* dex文件
*/
private DexFileReader dex;
/**
* 输入流
*/
private DataIn in;
/**
* 标签映射,指令位置->指令编号
*/
/* default */ Map<Integer, DexLabel> labels = new TreeMap<Integer, DexLabel>();
private boolean isStatic;
/**
* 方法的描述
*/
private Method method;
/**
* @param dex dex文件
* @param in 输入流
* @param isStatic
* @param method 方法的描述
*/
/* default */DexCodeReader(DexFileReader dex, DataIn in, boolean isStatic, Method method) {
this.dex = dex;
this.in = in;
this.method = method;
this.isStatic = isStatic;
}
private void findLabels(DataIn in, int instruction_size) {
int baseOffset = in.getCurrentPosition();
fixIssue130(in, instruction_size);
for (int currentOffset = (in.getCurrentPosition() - baseOffset) / 2; currentOffset < instruction_size; currentOffset = (in
.getCurrentPosition() - baseOffset) / 2) {
int opcode = in.readUShortx();
int uOpcodeH = opcode >> 8;
{
int uOpcodeL = opcode & 0xFF;
if (uOpcodeL != 0xFF) {
opcode = uOpcodeL;
}
}
OpcodeFormat format = OpcodeFormat.get(opcode, dex.apiLevel);
if (format == null) {
return;
}
try {
switch (opcode) {
case OP_GOTO:// 10t
order(currentOffset + (byte) uOpcodeH);
break;
case OP_IF_EQZ:// 21t
case OP_IF_NEZ:
case OP_IF_LTZ:
case OP_IF_GEZ:
case OP_IF_GTZ:
case OP_IF_LEZ:
case OP_IF_EQ:// 22t
case OP_IF_NE:
case OP_IF_LT:
case OP_IF_GE:
case OP_IF_GT:
case OP_IF_LE:
case DexInternalOpcode.OP_GOTO_16:// 20t;
order(currentOffset + in.readShortx());
break;
case DexInternalOpcode.OP_GOTO_32:// 30t;
order(currentOffset + xInt(in));
break;
case OP_SPARSE_SWITCH:
case OP_PACKED_SWITCH: {
int offset = xInt(in);
in.push();
try {
in.skip((offset - 3) * 2);
switch (opcode) {
case OP_SPARSE_SWITCH: {
in.skip(2);
int switch_size = in.readUShortx();
in.skip(4 * switch_size);// skip keys
for (int j = 0; j < switch_size; j++) {
order(currentOffset + in.readIntx());
}
order(currentOffset + 3);
}
break;
case OP_PACKED_SWITCH: {
in.skip(2);
int switch_size = in.readUShortx();
in.skip(4);
for (int j = 0; j < switch_size; j++) {
int targetOffset = in.readIntx();
order(currentOffset + targetOffset);
}
order(currentOffset + 3);
}
break;
}
} finally {
in.pop();
}
break;
}
case OP_NOP: {// OP_NOP
switch (uOpcodeH) {
case 0: // 0000 //spacer
break;
case 1: // packed-switch-data
{
int switch_size = in.readUShortx(); // switch_size
in.skip(switch_size * 4 + 4);
break;
}
case 2:// sparse-switch-data
{
int switch_size = in.readUShortx();
in.skip(switch_size * 8);
break;
}
case 3: {
int element_width = in.readUShortx();
int size = in.readUIntx();
// total byte of fill-array-data is ((size * element_width + 1) / 2 + 4) * 2;
in.skip((size * element_width + 1) & 0xFFFFFFFE);
break;
}
}
break;
}
default: {
in.skip(2 * format.getSize() - 2);
break;
}
}
} catch (Exception e) {
throw new DexException(e, String.format("while scan for label, Posotion :%04x", currentOffset));
}
}
}
/**
* This is a trick to fix issue 130 http://code.google.com/p/dex2jar/issues/detail?id=130
* <p/>
* <pre>
* 036280: 3200 0900 |0000: if-eq v0, v0, 0009 // +0009
* 036284: 2600 0300 0000 |0002: fill-array-data v0, 00000005 // +00000003
* 03628a: 0003 0100 0800 0000 7010 ce0b 0000 ... |0005: array-data (8 units)
* </pre>
* <p/>
* skip the if-eq, and direct read from 0009
*
* @param in
* @param instruction_size
*/
private void fixIssue130(DataIn in, int instruction_size) {
if (instruction_size < 4) {
return;
}
int base = in.getCurrentPosition();
int opcode = in.readUShortx();
int uOpcodeH = opcode >> 8;
{
int uOpcodeL = opcode & 0xFF;
if (uOpcodeL != 0xFF) {
opcode = uOpcodeL;
}
}
if ((opcode == OP_IF_EQ) && ((uOpcodeH & 0xF) == (uOpcodeH >> 4))) {
int offset = in.readShortx();
in.skip(offset * 2 - 4);
} else {
in.move(base);
}
}
private void findTryCatch(DataIn in, DexCodeVisitor dcv, int tries_size, int insn_size) {
int encoded_catch_handler_list = in.getCurrentPosition() + tries_size * 8;
for (int i = 0; i < tries_size; i++) {
int start_addr = in.readUIntx();
int insn_count = in.readUShortx();
int handler_offset = in.readUShortx();
if (start_addr > insn_size) {
continue;
}
order(start_addr);
int end = start_addr + insn_count;
order(end);
in.pushMove(encoded_catch_handler_list + handler_offset);// move to encoded_catch_handler
try {
boolean catchAll = false;
int listSize = (int) in.readLeb128();
int handlerCount = listSize;
if (listSize <= 0) {
listSize = -listSize;
handlerCount = listSize + 1;
catchAll = true;
}
DexLabel labels[] = new DexLabel[handlerCount];
String types[] = new String[handlerCount];
for (int k = 0; k < listSize; k++) {
int type_id = (int) in.readULeb128();
int handler = (int) in.readULeb128();
order(handler);
types[k] = dex.getType(type_id);
labels[k] = this.labels.get(handler);
}
if (catchAll) {
int handler = (int) in.readULeb128();
order(handler);
labels[listSize] = this.labels.get(handler);
}
dcv.visitTryCatch(this.labels.get(start_addr), this.labels.get(end), labels, types);
} finally {
in.pop();
}
}
}
/**
* 处理指令
*
* @param dcv
*/
public int accept(DexCodeVisitor dcv, int config) {
DataIn in = this.in;
int total_registers_size = in.readUShortx();
int in_register_size = in.readUShortx();
in.skip(2);// outs_size
int tries_size = in.readUShortx();
int debug_off = in.readUIntx();
int instruction_size = in.readUIntx();
LocalVariable localVariables[] = new LocalVariable[total_registers_size];
int args[];
// 处理方法的参数
{
int args_index;
int i = total_registers_size - in_register_size;
String[] parameterTypes = method.getParameterTypes();
if (!isStatic) {
args = new int[parameterTypes.length + 1];
localVariables[i] = new LocalVariable(i, 0, -1, "this", method.getOwner(), null);
args[0] = i++;
args_index = 1;
} else {
args = new int[parameterTypes.length];
args_index = 0;
}
for (String type : parameterTypes) {
localVariables[i] = new LocalVariable(i, 0, -1, "arg" + args_index, type, null);
args[args_index++] = i++;
if ("D".equals(type) || "J".equals(type)) {// 为Double/Long型特殊处理
i++;
}
}
dcv.visitArguments(total_registers_size, args);
}
// 处理异常处理
if (tries_size > 0) {
in.push();
try {
in.skip(instruction_size * 2);
if ((instruction_size & 0x01) != 0) {// skip padding
in.skip(2);
}
findTryCatch(in, dcv, tries_size, instruction_size);
} finally {
in.pop();
}
}
// 处理debug信息
if (debug_off != 0 && (0 == (config & DexFileReader.SKIP_DEBUG))) {
in.pushMove(debug_off);
try {
new DexDebugInfoReader(in, dex, instruction_size, this, localVariables, args).accept(dcv);
} finally {
in.pop();
}
}
// 查找标签
in.push();
try {
findLabels(in, instruction_size);
} finally {
in.pop();
}
DexOpcodeAdapter n = new DexOpcodeAdapter(this.dex, this.labels, dcv);
int tmp = acceptInsn(in, instruction_size, n);
if (tmp == -1) {
return -1;
}
dcv.visitEnd();
return 1;
}
// 处理指令
private int acceptInsn(DataIn in, int instruction_size, DexOpcodeAdapter n) {
Iterator<Integer> labelOffsetIterator = this.labels.keySet().iterator();
Integer nextLabelOffset = labelOffsetIterator.hasNext() ? labelOffsetIterator.next() : null;
int baseOffset = in.getCurrentPosition();
fixIssue130(in, instruction_size);
for (int currentOffset = (in.getCurrentPosition() - baseOffset) / 2; currentOffset < instruction_size; currentOffset = (in
.getCurrentPosition() - baseOffset) / 2) {
boolean currentOffsetVisited = false;
while (nextLabelOffset != null) {// issue 65, a label may `inside` an instruction
int _intNextLabelOffset = nextLabelOffset;// autobox
if (_intNextLabelOffset > currentOffset) {
break;
} else if (_intNextLabelOffset == currentOffset) {
currentOffsetVisited = true;
n.offset(currentOffset);
nextLabelOffset = labelOffsetIterator.hasNext() ? labelOffsetIterator.next() : null;
break;
} else {// _intNextLabelOffset < currentOffset
n.offset(_intNextLabelOffset);
nextLabelOffset = labelOffsetIterator.hasNext() ? labelOffsetIterator.next() : null;
}
}
if (!currentOffsetVisited) {
n.offset(currentOffset);
}
int opcode = in.readUShortx();
int uOpcodeH = opcode >> 8;
{
int uOpcodeL = opcode & 0xFF;
if (uOpcodeL != 0xFF) {
opcode = uOpcodeL;
}
}
OpcodeFormat format = OpcodeFormat.get(opcode, dex.apiLevel);
if (format == null) {
return baseOffset;
}
switch (format) {
case F10t:
n.x0t(opcode, (byte) (uOpcodeH));
break;
case F10x: {
switch (opcode) {
case OP_NOP:// OP_NOP
switch (uOpcodeH) {
case 0: // 0000 //spacer
break;
case 1: // packed-switch-data
{
int switch_size = in.readUShortx(); // switch_size
in.skip(switch_size * 4 + 4);
break;
}
case 2:// sparse-switch-data
{
int switch_size = in.readUShortx();
in.skip(switch_size * 8);
break;
}
case 3: {// fill-array-data
int element_width = in.readUShortx();
int size = in.readUIntx();
// total byte of fill-array-data is ((size * element_width + 1) / 2 + 4) * 2;
in.skip((size * element_width + 1) & 0xFFFFFFFE);
break;
}
}
break;
}
n.x0x(opcode);
break;
}
case F11n: {
int VV = (byte) (uOpcodeH);
int B = VV >> 4;
n.x1n(opcode, VV & 0xF, B);
break;
}
case F11x:
n.x1x(opcode, uOpcodeH);
break;
case F12x: {
n.x2x(opcode, uOpcodeH & 0xF, uOpcodeH >> 4);
break;
}
case F20bc: {
n.x0bc(opcode, uOpcodeH, in.readUShortx());
break;
}
case F20t:
n.x0t(opcode, in.readShortx());
break;
case F21c: {
n.x1c(opcode, uOpcodeH, in.readUShortx());
break;
}
case F21h: {
n.x1h(opcode, uOpcodeH, in.readShortx());
break;
}
case F21s: {
n.x1s(opcode, uOpcodeH, in.readShortx());
break;
}
case F21t: {
n.x1t(opcode, uOpcodeH, in.readShortx());
break;
}
case F22b: {
int tmp = in.readShortx();
n.x2b(opcode, uOpcodeH, tmp & 0xFF, tmp >> 8);
break;
}
case F22c: {
n.x2c(opcode, uOpcodeH & 0xF, uOpcodeH >> 4, in.readUShortx());
break;
}
case F22cs: {
n.x2cs(opcode, uOpcodeH & 0xF, uOpcodeH >> 4, in.readUShortx());
break;
}
case F22s: {
n.x2s(opcode, uOpcodeH & 0xF, uOpcodeH >> 4, in.readShortx());
break;
}
case F22t: {
n.x2t(opcode, uOpcodeH & 0xF, uOpcodeH >> 4, in.readShortx());
break;
}
case F22x: {
n.x2x(opcode, uOpcodeH, in.readUShortx());
break;
}
case F23x: {
int tmp = in.readUShortx();
n.x3x(opcode, uOpcodeH, tmp & 0xFF, tmp >> 8);
break;
}
case F30t:
n.x0t(opcode, xUint(in));
break;
case F31c: {
n.x1c(opcode, uOpcodeH, xUint(in));
break;
}
case F31i: {
n.x1i(opcode, uOpcodeH, xInt(in));
break;
}
case F31t: {
int BBBBBBBB = xInt(in);
switch (opcode) {
case OP_FILL_ARRAY_DATA:
case OP_PACKED_SWITCH:
case OP_SPARSE_SWITCH:
in.push();
try {
in.skip((BBBBBBBB - 3) * 2);
switch (opcode) {
case OP_SPARSE_SWITCH: {
in.skip(2);
int switch_size = in.readUShortx();
int cases[] = new int[switch_size];
int label[] = new int[switch_size];
for (int j = 0; j < switch_size; j++) {
cases[j] = in.readIntx();
}
for (int j = 0; j < switch_size; j++) {
label[j] = in.readIntx();
}
n.visitLookupSwitchStmt(opcode, uOpcodeH, 3, cases, label);
}
break;
case OP_PACKED_SWITCH: {
in.skip(2);
int switch_size = in.readUShortx();
int first_case = in.readIntx();
int last_case = first_case - 1 + switch_size;
int _labels[] = new int[switch_size];
for (int j = 0; j < switch_size; j++) {
int targetOffset = in.readIntx();
_labels[j] = targetOffset;
}
n.visitTableSwitchStmt(opcode, uOpcodeH, 3, first_case, last_case, _labels);
}
break;
case OP_FILL_ARRAY_DATA: {
in.skip(2);
/* int elemWidth = in.readUShortx();
int initLength = in.readUIntx();
// add
if (initLength >= Integer.MAX_VALUE) {
return -1;
}
Object[] values = new Object[initLength];
switch (elemWidth) {
case 1:
for (int j = 0; j < initLength; j++) {
values[j] = (byte) in.readByte();
}
break;
case 2:
for (int j = 0; j < initLength; j++) {
values[j] = (short) in.readShortx();
}
break;
case 4:
for (int j = 0; j < initLength; j++) {
values[j] = in.readIntx();
}
break;
case 8:
for (int j = 0; j < initLength; j++) {
values[j] = (in.readIntx() & 0x00000000FFFFFFFFL) | (((long) in.readIntx()) << 32);
}
break;
}
n.visitFillArrayStmt(opcode, uOpcodeH, elemWidth, initLength, values);
*/}
}
} finally {
in.pop();
}
break;
default:
n.x1t(opcode, uOpcodeH, BBBBBBBB);
}
break;
}
case F32s: {
n.x2s(opcode, uOpcodeH, in.readUByte(), in.readShortx());
break;
}
case F32x: {
n.x2x(opcode, in.readUShortx(), in.readUShortx());
break;
}
case F33x: {
int tmp = in.readUShortx();
n.x3x(opcode, tmp & 0xFF, tmp >> 8, in.readUShortx());
break;
}
case F35c: {
int g = uOpcodeH & 0xF;
int a = uOpcodeH >> 4;
int bbbb = in.readUShortx();
int vvvv = in.readUShortx();
int c = vvvv & 0xF;
int d = (vvvv >> 4) & 0xF;
int e = (vvvv >> 8) & 0xF;
int f = vvvv >> 12;
n.x5c(opcode, a, c, d, e, f, g, bbbb);
break;
}
case F35mi: {
int g = uOpcodeH & 0xF;
int a = uOpcodeH >> 4;
int bbbb = in.readUShortx();
int vvvv = in.readUShortx();
int c = vvvv & 0xF;
int d = (vvvv >> 4) & 0xF;
int e = (vvvv >> 8) & 0xF;
int f = vvvv >> 12;
n.x5mi(opcode, a, c, d, e, f, g, bbbb);
break;
}
case F35ms: {
int g = uOpcodeH & 0xF;
int a = uOpcodeH >> 4;
int bbbb = in.readUShortx();
int vvvv = in.readUShortx();
int c = vvvv & 0xF;
int d = (vvvv >> 4) & 0xF;
int e = (vvvv >> 8) & 0xF;
int f = vvvv >> 12;
n.x5ms(opcode, a, c, d, e, f, g, bbbb);
break;
}
case F3rc: {
n.xrc(opcode, uOpcodeH, in.readUShortx(), in.readUShortx());
break;
}
case F3rmi: {
n.xrmi(opcode, uOpcodeH, in.readUShortx(), in.readUShortx());
break;
}
case F3rms: {
n.xrms(opcode, uOpcodeH, in.readUShortx(), in.readUShortx());
break;
}
case F40sc: {
int bbbb_bbbb = xUint(in);
int aaaa = in.readUShortx();
n.x0sc(opcode, aaaa, bbbb_bbbb);
break;
}
case F41c: {
int bbbb_bbbb = xUint(in);
int aaaa = in.readUShortx();
n.x1c(opcode, aaaa, bbbb_bbbb);
break;
}
case F51l: {
n.x1l(opcode, uOpcodeH, xLong(in));
break;
}
case F52c: {
int cccc_cccc = xUint(in);
int aaaa = in.readUShortx();
int bbbb = in.readUShortx();
n.x2c(opcode, aaaa, bbbb, cccc_cccc);
break;
}
case F5rc: {
int bbbb_bbbb = xUint(in);
int aaaa = in.readUShortx();
int cccc = in.readUShortx();
n.xrc(opcode, aaaa, bbbb_bbbb, cccc);
break;
}
}
}
while (nextLabelOffset != null) {
n.offset(nextLabelOffset);
if (labelOffsetIterator.hasNext()) {
nextLabelOffset = labelOffsetIterator.next();
} else {
break;
}
}
return 1;
}
private static long xLong(DataIn in) {
long rs = in.readUShortx();
rs |= ((long) in.readUShortx()) << 16;
rs |= ((long) in.readUShortx()) << 32;
rs |= ((long) in.readUShortx()) << 48;
return rs;
}
private static int xInt(DataIn in) {
return in.readUShortx() | (in.readUShortx() << 16);
}
private static int xUint(DataIn in) {
return in.readUShortx() | (in.readUShortx() << 16);
}
/**
* 预定一个标签位置
*
* @param offset 指令位置
*/
void order(int offset) {
if (!labels.containsKey(offset)) {
labels.put(offset, new DexLabel(offset));
}
}
}