com.googlecode.d2j.reader.DexFileReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle Show documentation
Show all versions of gradle Show documentation
fakeradnroid gradle builder
/*
* 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.d2j.reader;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import com.googlecode.d2j.*;
import com.googlecode.d2j.node.DexAnnotationNode;
import com.googlecode.d2j.util.Mutf8;
import com.googlecode.d2j.visitors.*;
import static com.googlecode.d2j.DexConstants.*;
/**
* Open and read a dex file.this is the entrance of dex-reader. to read a dex/odex, use the following code:
*
*
* DexFileVisitor visitor = new xxxFileVisitor();
* DexFileReader reader = new DexFileReader(dexFile);
* reader.accept(visitor);
*
*
* @author Panxiaobo
* @version $Rev$
*/
public class DexFileReader implements BaseDexFileReader {
/**
* skip debug infos in dex file.
*/
public static final int SKIP_DEBUG = 1;
/**
* skip code info in dex file, this indicate {@link #SKIP_DEBUG}
*/
public static final int SKIP_CODE = 1 << 2;
/**
* skip annotation info in dex file.
*/
public static final int SKIP_ANNOTATION = 1 << 3;
/**
* skip field constant in dex file.
*/
public static final int SKIP_FIELD_CONSTANT = 1 << 4;
/**
* ingore read exception
*/
public static final int IGNORE_READ_EXCEPTION = 1 << 5;
/**
* read all methods, even if they are glitch
*/
public static final int KEEP_ALL_METHODS = 1 << 6;
/**
* keep clinit method when {@link #SKIP_DEBUG}
*/
public static final int KEEP_CLINIT = 1 << 7;
/**
* keep clinit method when {@link #SKIP_DEBUG}
*/
public static final int SKIP_EXCEPTION = 1 << 8;
// private static final int REVERSE_ENDIAN_CONSTANT = 0x78563412;
static final int DBG_END_SEQUENCE = 0x00;
static final int DBG_ADVANCE_PC = 0x01;
static final int DBG_ADVANCE_LINE = 0x02;
static final int DBG_START_LOCAL = 0x03;
static final int DBG_START_LOCAL_EXTENDED = 0x04;
static final int DBG_END_LOCAL = 0x05;
static final int DBG_RESTART_LOCAL = 0x06;
static final int DBG_SET_PROLOGUE_END = 0x07;
static final int DBG_SET_EPILOGUE_BEGIN = 0x08;
static final int DBG_SET_FILE = 0x09;
static final int DBG_FIRST_SPECIAL = 0x0a;
static final int DBG_LINE_BASE = -4;
static final int DBG_LINE_RANGE = 15;
private static final int ENDIAN_CONSTANT = 0x12345678;
private static final int VALUE_BYTE = 0x00;
private static final int VALUE_SHORT = 0x02;
private static final int VALUE_CHAR = 0x03;
private static final int VALUE_INT = 0x04;
private static final int VALUE_LONG = 0x06;
private static final int VALUE_FLOAT = 0x10;
private static final int VALUE_DOUBLE = 0x11;
private static final int VALUE_METHOD_TYPE = 0x15;
private static final int VALUE_METHOD_HANDLE = 0x16;
private static final int VALUE_STRING = 0x17;
private static final int VALUE_TYPE = 0x18;
private static final int VALUE_FIELD = 0x19;
private static final int VALUE_METHOD = 0x1a;
private static final int VALUE_ENUM = 0x1b;
private static final int VALUE_ARRAY = 0x1c;
private static final int VALUE_ANNOTATION = 0x1d;
private static final int VALUE_NULL = 0x1e;
private static final int VALUE_BOOLEAN = 0x1f;
private static final int TYPE_CALL_SITE_ID_ITEM = 0x0007;
private static final int TYPE_METHOD_HANDLE_ITEM = 0x0008;
final ByteBuffer annotationSetRefListIn;
final ByteBuffer annotationsDirectoryItemIn;
final ByteBuffer annotationSetItemIn;
final ByteBuffer annotationItemIn;
final ByteBuffer classDataIn;
final ByteBuffer codeItemIn;
final ByteBuffer encodedArrayItemIn;
final ByteBuffer stringIdIn;
final ByteBuffer typeIdIn;
final ByteBuffer protoIdIn;
final ByteBuffer fieldIdIn;
final ByteBuffer methoIdIn;
final ByteBuffer classDefIn;
final ByteBuffer typeListIn;
final ByteBuffer stringDataIn;
final ByteBuffer debugInfoIn;
final ByteBuffer callSiteIdIn;
final ByteBuffer methodHandleIdIn;
final int string_ids_size;
final int type_ids_size;
final int proto_ids_size;
final int field_ids_size;
final int method_ids_size;
final private int class_defs_size;
final int call_site_ids_size;
final int method_handle_ids_size;
final int dex_version;
/**
* read dex from a {@link ByteBuffer}.
*
* @param in
*/
public DexFileReader(ByteBuffer in) {
in.position(0);
in = in.asReadOnlyBuffer().order(ByteOrder.BIG_ENDIAN);
int magic = in.getInt() & 0xFFFFFF00;
final int MAGIC_DEX = 0x6465780A & 0xFFFFFF00;// hex for 'dex ', ignore the 0A
final int MAGIC_ODEX = 0x6465790A & 0xFFFFFF00;// hex for 'dey ', ignore the 0A
if (magic == MAGIC_DEX) {
;
} else if (magic == MAGIC_ODEX) {
throw new DexException("Not support odex");
} else {
throw new DexException("not support magic.");
}
int version = in.getInt() >> 8;
if (version < 0 || version < DEX_035) {
throw new DexException("not support version.");
}
this.dex_version = version;
in.order(ByteOrder.LITTLE_ENDIAN);
// skip uint checksum
// and 20 bytes signature
// and uint file_size
// and uint header_size 0x70
skip(in, 4 + 20 + 4 + 4);
int endian_tag = in.getInt();
if (endian_tag != ENDIAN_CONSTANT) {
throw new DexException("not support endian_tag");
}
// skip uint link_size
// and uint link_off
skip(in, 4 + 4);
int map_off = in.getInt();
string_ids_size = in.getInt();
int string_ids_off = in.getInt();
type_ids_size = in.getInt();
int type_ids_off = in.getInt();
proto_ids_size = in.getInt();
int proto_ids_off = in.getInt();
field_ids_size = in.getInt();
int field_ids_off = in.getInt();
method_ids_size = in.getInt();
int method_ids_off = in.getInt();
class_defs_size = in.getInt();
int class_defs_off = in.getInt();
// skip uint data_size data_off
int call_site_ids_off = 0;
int call_site_ids_size = 0;
int method_handle_ids_off = 0;
int method_handle_ids_size = 0;
if (dex_version > DEX_037) {
in.position(map_off);
int size = in.getInt();
for (int i = 0; i < size; i++) {
int type = in.getShort() & 0xFFFF;
in.getShort(); // unused;
int item_size = in.getInt();
int item_offset = in.getInt();
switch (type) {
case TYPE_CALL_SITE_ID_ITEM:
call_site_ids_off = item_offset;
call_site_ids_size = item_size;
break;
case TYPE_METHOD_HANDLE_ITEM:
method_handle_ids_off = item_offset;
method_handle_ids_size = item_size;
break;
default:
break;
}
}
}
this.call_site_ids_size = call_site_ids_size;
this.method_handle_ids_size = method_handle_ids_size;
stringIdIn = slice(in, string_ids_off, string_ids_size * 4);
typeIdIn = slice(in, type_ids_off, type_ids_size * 4);
protoIdIn = slice(in, proto_ids_off, proto_ids_size * 12);
fieldIdIn = slice(in, field_ids_off, field_ids_size * 8);
methoIdIn = slice(in, method_ids_off, method_ids_size * 8);
classDefIn = slice(in, class_defs_off, class_defs_size * 32);
callSiteIdIn = slice(in, call_site_ids_off, call_site_ids_size * 4);
methodHandleIdIn = slice(in, method_handle_ids_off, method_handle_ids_size * 8);
in.position(0);
annotationsDirectoryItemIn = in.duplicate().order(ByteOrder.LITTLE_ENDIAN);
annotationSetItemIn = in.duplicate().order(ByteOrder.LITTLE_ENDIAN);
annotationItemIn = in.duplicate().order(ByteOrder.LITTLE_ENDIAN);
annotationSetRefListIn = in.duplicate().order(ByteOrder.LITTLE_ENDIAN);
classDataIn = in.duplicate().order(ByteOrder.LITTLE_ENDIAN);
codeItemIn = in.duplicate().order(ByteOrder.LITTLE_ENDIAN);
stringDataIn = in.duplicate().order(ByteOrder.LITTLE_ENDIAN);
encodedArrayItemIn = in.duplicate().order(ByteOrder.LITTLE_ENDIAN);
typeListIn = in.duplicate().order(ByteOrder.LITTLE_ENDIAN);
debugInfoIn = in.duplicate().order(ByteOrder.LITTLE_ENDIAN);
}
/**
*
* @param data
* the byte array of dex
* @return
*/
public DexFileReader(byte[] data) {
this(ByteBuffer.wrap(data));
}
/**
*
* @param file
* the dex file
* @throws IOException
*/
public DexFileReader(File file) throws IOException {
this(file.toPath());
}
public DexFileReader(Path file) throws IOException {
this(Files.readAllBytes(file));
}
public DexFileReader(InputStream is) throws IOException {
this(toByteArray(is));
}
/**
* Reads a string index. String indicies are offset by 1, and a 0 value in the stream (-1 as returned by this
* method) means "null"
*
* @return index into file's string ids table, -1 means null
*/
private static int readStringIndex(ByteBuffer bs) {
int offsetIndex = readULeb128i(bs);
return offsetIndex - 1;
}
private static byte[] toByteArray(InputStream is) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
for (int c = is.read(buff); c > 0; c = is.read(buff)) {
out.write(buff, 0, c);
}
return out.toByteArray();
}
private static ByteBuffer slice(ByteBuffer in, int offset, int length) {
in.position(offset);
ByteBuffer b = in.slice();
b.limit(length);
b.order(ByteOrder.LITTLE_ENDIAN);
return b;
}
private static void skip(ByteBuffer in, int bytes) {
in.position(in.position() + bytes);
}
public static void niceExceptionMessage(Throwable t, int deep) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < deep + 1; i++) {
sb.append(".");
}
sb.append(' ');
if (t instanceof DexException) {
sb.append(t.getMessage());
System.err.println(sb.toString());
if (t.getCause() != null) {
niceExceptionMessage(t.getCause(), deep + 1);
}
} else {
if (t != null) {
System.err.println(sb.append("ROOT cause:").toString());
t.printStackTrace(System.err);
}
}
}
private static long readIntBits(ByteBuffer in, int before) {
int length = ((before >> 5) & 0x7) + 1;
long value = 0;
for (int j = 0; j < length; j++) {
value |= ((long) (0xFF & in.get())) << (j * 8);
}
int shift = (8 - length) * 8;
return value << shift >> shift;
}
private static long readUIntBits(ByteBuffer in, int before) {
int length = ((before >> 5) & 0x7) + 1;
long value = 0;
for (int j = 0; j < length; j++) {
value |= ((long) (0xFF & in.get())) << (j * 8);
}
return value;
}
private static long readFloatBits(ByteBuffer in, int before) {
int bytes = ((before >> 5) & 0x7) + 1;
long result = 0L;
for (int i = 0; i < bytes; ++i) {
result |= ((long) (0xFF & in.get())) << (i * 8);
}
result <<= (8 - bytes) * 8;
return result;
}
static int sshort(byte[] data, int offset) {
return (data[offset + 1] << 8) | (0xFF & data[offset]);
}
static int ushort(byte[] data, int offset) {
return ((0xFF & data[offset + 1]) << 8) | (0xFF & data[offset]);
}
static int sint(byte[] data, int offset) {
return (data[offset + 3] << 24) | ((0xFF & data[offset + 2]) << 16) | ((0xFF & data[offset + 1]) << 8)
| ((0xFF & data[offset]));
}
static int uint(byte[] data, int offset) {
return sint(data, offset);
}
static void WARN(String fmt, Object... args) {
System.err.println(String.format(fmt, args));
}
static int ubyte(byte[] insns, int offset) {
return 0xFF & insns[offset];
}
static int sbyte(byte[] insns, int offset) {
return insns[offset];
}
private static void order(Map labelsMap, int offset) {
if (!labelsMap.containsKey(offset)) {
labelsMap.put(offset, new DexLabel(offset));
}
}
public static int readULeb128i(ByteBuffer in) {
int value = 0;
int count = 0;
int b = in.get();
while ((b & 0x80) != 0) {
value |= (b & 0x7f) << count;
count += 7;
b = in.get();
}
value |= (b & 0x7f) << count;
return value;
}
public static int readLeb128i(ByteBuffer in) {
int bitpos = 0;
int vln = 0;
do {
int inp = in.get();
vln |= (inp & 0x7F) << bitpos;
bitpos += 7;
if ((inp & 0x80) == 0) {
break;
}
} while (true);
if (((1L << (bitpos - 1)) & vln) != 0) {
vln -= (1L << bitpos);
}
return vln;
}
private static void DEBUG_DEBUG(String fmt, Object... args) {
// System.out.println(String.format(fmt, args));
}
private void read_debug_info(int offset, int regSize, boolean isStatic, Method method,
Map labelMap, DexDebugVisitor dcv) {
ByteBuffer in = debugInfoIn;
in.position(offset);
int address = 0;
int line = readULeb128i(in);
int szParams = readULeb128i(in);
LocalEntry lastEntryForReg[] = new LocalEntry[regSize];
int argsSize = 0;
for (String paramType : method.getParameterTypes()) {
if (paramType.equals("J") || paramType.equals("D")) {
argsSize += 2;
} else {
argsSize += 1;
}
}
int curReg = regSize - argsSize;
if (!isStatic) {
// Start off with implicit 'this' entry
LocalEntry thisEntry = new LocalEntry("this", method.getOwner(), null);
lastEntryForReg[curReg - 1] = thisEntry;
// dcv.visitParameterName(curReg - 1, "this");
DEBUG_DEBUG("v%d :%s, %s", curReg - 1, "this", method.getOwner());
}
String[] params = method.getParameterTypes();
for (int i = 0; i < szParams; i++) {
String paramType = params[i];
LocalEntry le;
int nameIdx = readStringIndex(in);
String name = getString(nameIdx);
le = new LocalEntry(name, paramType);
lastEntryForReg[curReg] = le;
if (name != null) {
dcv.visitParameterName(i, name);
}
DEBUG_DEBUG("v%d :%s, %s", curReg, name, paramType);
curReg += 1;
if (paramType.equals("J") || paramType.equals("D")) {
curReg += 1;
}
}
for (;;) {
int opcode = in.get() & 0xff;
switch (opcode) {
case DBG_START_LOCAL: {
int reg = readULeb128i(in);
int nameIdx = readStringIndex(in);
int typeIdx = readStringIndex(in);
String name = getString(nameIdx);
String type = getType(typeIdx);
DEBUG_DEBUG("Start: v%d :%s, %s", reg, name, type);
LocalEntry le = new LocalEntry(name, type);
lastEntryForReg[reg] = le;
order(labelMap, address);
dcv.visitStartLocal(reg, labelMap.get(address), name, type, null);
}
break;
case DBG_START_LOCAL_EXTENDED: {
int reg = readULeb128i(in);
int nameIdx = readStringIndex(in);
int typeIdx = readStringIndex(in);
int sigIdx = readStringIndex(in);
String name = getString(nameIdx);
String type = getType(typeIdx);
String signature = getString(sigIdx);
DEBUG_DEBUG("Start: v%d :%s, %s // %s", reg, name, type, signature);
LocalEntry le = new LocalEntry(name, type, signature);
order(labelMap, address);
dcv.visitStartLocal(reg, labelMap.get(address), name, type, signature);
lastEntryForReg[reg] = le;
}
break;
case DBG_RESTART_LOCAL: {
int reg = readULeb128i(in);
LocalEntry le = lastEntryForReg[reg];
if (le == null) {
throw new RuntimeException("Encountered RESTART_LOCAL on new v" + reg);
}
if (le.signature == null) {
DEBUG_DEBUG("Start: v%d :%s, %s", reg, le.name, le.type);
} else {
DEBUG_DEBUG("Start: v%d :%s, %s // %s", reg, le.name, le.type, le.signature);
}
order(labelMap, address);
dcv.visitRestartLocal(reg, labelMap.get(address));
}
break;
case DBG_END_LOCAL: {
int reg = readULeb128i(in);
LocalEntry le = lastEntryForReg[reg];
if (le == null) {
throw new RuntimeException("Encountered RESTART_LOCAL on new v" + reg);
}
if (le.signature == null) {
DEBUG_DEBUG("End: v%d :%s, %s", reg, le.name, le.type);
} else {
DEBUG_DEBUG("End: v%d :%s, %s // %s", reg, le.name, le.type, le.signature);
}
order(labelMap, address);
dcv.visitEndLocal(reg, labelMap.get(address));
}
break;
case DBG_END_SEQUENCE:
// all done
return;
case DBG_ADVANCE_PC:
address += readULeb128i(in);
break;
case DBG_ADVANCE_LINE:
line += readLeb128i(in);
break;
case DBG_SET_PROLOGUE_END:
order(labelMap, address);
dcv.visitPrologue(labelMap.get(address));
break;
case DBG_SET_EPILOGUE_BEGIN:
order(labelMap, address);
dcv.visitEpiogue(labelMap.get(address));
break;
case DBG_SET_FILE:
// skip
break;
default:
if (opcode < DBG_FIRST_SPECIAL) {
throw new RuntimeException("Invalid extended opcode encountered " + opcode);
}
int adjopcode = opcode - DBG_FIRST_SPECIAL;
address += adjopcode / DBG_LINE_RANGE;
line += DBG_LINE_BASE + (adjopcode % DBG_LINE_RANGE);
order(labelMap, address);
dcv.visitLineNumber(line, labelMap.get(address));
break;
}
}
}
@Override
public int getDexVersion() {
return dex_version;
}
/**
* equals to {@link #accept(DexFileVisitor, int)} with 0 as config
*
* @param dv
*/
@Override
public void accept(DexFileVisitor dv) {
this.accept(dv, 0);
}
@Override
public List getClassNames() {
List names = new ArrayList<>(class_defs_size);
ByteBuffer in = classDefIn;
for (int cid = 0; cid < class_defs_size; cid++) {
in.position(cid * 32);
String className = this.getType(in.getInt());
names.add(className);
}
return names;
}
/**
* Makes the given visitor visit the dex file.
*
* @param dv
* visitor
* @param config
* config flags, {@link #SKIP_CODE}, {@link #SKIP_DEBUG}, {@link #SKIP_ANNOTATION},
* {@link #SKIP_FIELD_CONSTANT}
*/
@Override
public void accept(DexFileVisitor dv, int config) {
dv.visitDexFileVersion(this.dex_version);
for (int cid = 0; cid < class_defs_size; cid++) {
accept(dv, cid, config);
}
dv.visitEnd();
}
/**
* Makes the given visitor visit the dex file. Notice the
* {@link DexFileVisitor#visitEnd()} is not called
*
* @param dv
* visitor
* @param classIdx
* index of class_def
* @param config
* config flags, {@link #SKIP_CODE}, {@link #SKIP_DEBUG}, {@link #SKIP_ANNOTATION},
* {@link #SKIP_FIELD_CONSTANT}
*/
@Override
public void accept(DexFileVisitor dv, int classIdx, int config) {
classDefIn.position(classIdx * 32);
int class_idx = classDefIn.getInt();
int access_flags = classDefIn.getInt();
int superclass_idx = classDefIn.getInt();
int interfaces_off = classDefIn.getInt();
int source_file_idx = classDefIn.getInt();
int annotations_off = classDefIn.getInt();
int class_data_off = classDefIn.getInt();
int static_values_off = classDefIn.getInt();
String className = getType(class_idx);
if(ignoreClass(className)) return;
String superClassName = getType(superclass_idx);
String[] interfaceNames = getTypeList(interfaces_off);
try {
DexClassVisitor dcv = dv.visit(access_flags, className, superClassName, interfaceNames);
if (dcv != null)
{
acceptClass(dcv, source_file_idx, annotations_off, class_data_off, static_values_off, config);
dcv.visitEnd();
}
} catch (Exception ex) {
DexException dexException = new DexException(ex, "Error process class: [%d]%s", class_idx, className);
if (0 != (config & IGNORE_READ_EXCEPTION)) {
niceExceptionMessage(dexException, 0);
} else {
throw dexException;
}
}
}
public Boolean ignoreClass(String className){
return false;
}
private Object readEncodedValue(ByteBuffer in) {
int b = 0xFF & in.get();
int type = b & 0x1f;
switch (type) {
case VALUE_BYTE:
return new Byte((byte) readIntBits(in, b));
case VALUE_SHORT:
return new Short((short) readIntBits(in, b));
case VALUE_CHAR:
return new Character((char) readUIntBits(in, b));
case VALUE_INT:
return new Integer((int) readIntBits(in, b));
case VALUE_LONG:
return new Long(readIntBits(in, b));
case VALUE_FLOAT:
return Float.intBitsToFloat((int) (readFloatBits(in, b) >> 32));
case VALUE_DOUBLE:
return Double.longBitsToDouble(readFloatBits(in, b));
case VALUE_METHOD_TYPE:
return getProto((int) readUIntBits(in, b));
case VALUE_METHOD_HANDLE:
return getMethodHandle((int) readUIntBits(in, b));
case VALUE_STRING:
return getString((int) readUIntBits(in, b));
case VALUE_TYPE: {
int type_id = (int) readUIntBits(in, b);
return new DexType(getType(type_id));
}
case VALUE_FIELD: {
int field_id = (int) readUIntBits(in, b);
return getField(field_id);
}
case VALUE_METHOD: {
int method_id = (int) readUIntBits(in, b);
return getMethod(method_id);
}
case VALUE_ENUM: {
return getField((int) readUIntBits(in, b));
}
case VALUE_ARRAY: {
return read_encoded_array(in);
}
case VALUE_ANNOTATION: {
return read_encoded_annotation(in);
}
case VALUE_NULL:
return null;
case VALUE_BOOLEAN: {
return new Boolean(((b >> 5) & 0x3) != 0);
}
default:
throw new DexException("Not support yet.");
}
}
private MethodHandle getMethodHandle(int i) {
methodHandleIdIn.position(i * 8);
int method_handle_type = methodHandleIdIn.getShort() & 0xFFFF;
methodHandleIdIn.getShort();//unused
int field_or_method_id = methodHandleIdIn.getShort() & 0xFFFF;
switch (method_handle_type) {
case MethodHandle.INSTANCE_GET:
case MethodHandle.INSTANCE_PUT:
case MethodHandle.STATIC_GET:
case MethodHandle.STATIC_PUT:
return new MethodHandle(method_handle_type, getField(field_or_method_id));
case MethodHandle.INVOKE_INSTANCE:
case MethodHandle.INVOKE_STATIC:
case MethodHandle.INVOKE_CONSTRUCTOR:
case MethodHandle.INVOKE_DIRECT:
case MethodHandle.INVOKE_INTERFACE:
return new MethodHandle(method_handle_type, getMethod(field_or_method_id));
default:
throw new RuntimeException();
}
}
private void acceptClass(DexClassVisitor dcv, int source_file_idx, int annotations_off, int class_data_off,
int static_values_off, int config) {
if ((config & SKIP_DEBUG) == 0) {
if (source_file_idx != -1) {
dcv.visitSource(this.getString(source_file_idx));
}
}
Map fieldAnnotationPositions;
Map methodAnnotationPositions;
Map paramAnnotationPositions;
if ((config & SKIP_ANNOTATION) == 0) {
// 获取注解
fieldAnnotationPositions = new HashMap();
methodAnnotationPositions = new HashMap();
paramAnnotationPositions = new HashMap();
if (annotations_off != 0) { // annotations_directory_item
annotationsDirectoryItemIn.position(annotations_off);
int class_annotations_off = annotationsDirectoryItemIn.getInt();
int field_annotation_size = annotationsDirectoryItemIn.getInt();
int method_annotation_size = annotationsDirectoryItemIn.getInt();
int parameter_annotation_size = annotationsDirectoryItemIn.getInt();
for (int i = 0; i < field_annotation_size; i++) {
int field_idx = annotationsDirectoryItemIn.getInt();
int field_annotations_offset = annotationsDirectoryItemIn.getInt();
fieldAnnotationPositions.put(field_idx, field_annotations_offset);
}
for (int i = 0; i < method_annotation_size; i++) {
int method_idx = annotationsDirectoryItemIn.getInt();
int method_annotation_offset = annotationsDirectoryItemIn.getInt();
methodAnnotationPositions.put(method_idx, method_annotation_offset);
}
for (int i = 0; i < parameter_annotation_size; i++) {
int method_idx = annotationsDirectoryItemIn.getInt();
int parameter_annotation_offset = annotationsDirectoryItemIn.getInt();
paramAnnotationPositions.put(method_idx, parameter_annotation_offset);
}
if (class_annotations_off != 0) {
try {
read_annotation_set_item(class_annotations_off, dcv);
} catch (Exception e) {
throw new DexException("error on reading Annotation of class ", e);
}
}
}
} else {
fieldAnnotationPositions = null;
methodAnnotationPositions = null;
paramAnnotationPositions = null;
}
if (class_data_off != 0) {
ByteBuffer in = classDataIn;
in.position(class_data_off);
int static_fields = (int) readULeb128i(in);
int instance_fields = (int) readULeb128i(in);
int direct_methods = (int) readULeb128i(in);
int virtual_methods = (int) readULeb128i(in);
{
int lastIndex = 0;
{
Object[] constant = null;
if ((config & SKIP_FIELD_CONSTANT) == 0) {
if (static_values_off != 0) {
constant = read_encoded_array_item(static_values_off);
}
}
for (int i = 0; i < static_fields; i++) {
Object value = null;
if (constant != null && i < constant.length) {
value = constant[i];
}
lastIndex = acceptField(in, lastIndex, dcv, fieldAnnotationPositions, value, config);
}
}
lastIndex = 0;
for (int i = 0; i < instance_fields; i++) {
lastIndex = acceptField(in, lastIndex, dcv, fieldAnnotationPositions, null, config);
}
lastIndex = 0;
boolean firstMethod = true;
for (int i = 0; i < direct_methods; i++) {
lastIndex = acceptMethod(in, lastIndex, dcv, methodAnnotationPositions, paramAnnotationPositions,
config, firstMethod);
firstMethod = false;
}
lastIndex = 0;
firstMethod = true;
for (int i = 0; i < virtual_methods; i++) {
lastIndex = acceptMethod(in, lastIndex, dcv, methodAnnotationPositions, paramAnnotationPositions,
config, firstMethod);
firstMethod = false;
}
}
}
}
private Object[] read_encoded_array_item(int static_values_off) {
encodedArrayItemIn.position(static_values_off);
return read_encoded_array(encodedArrayItemIn);
}
private Object[] read_encoded_array(ByteBuffer in) {
int size = readULeb128i(in);
Object[] constant = new Object[size];
for (int i = 0; i < size; i++) {
constant[i] = readEncodedValue(in);
}
return constant;
}
private void read_annotation_set_item(int offset, DexAnnotationAble daa) { // annotation_set_item
ByteBuffer in = annotationSetItemIn;
in.position(offset);
int size = in.getInt();
for (int j = 0; j < size; j++) {
int annotation_off = in.getInt();
read_annotation_item(annotation_off, daa);
}
}
private void read_annotation_item(int annotation_off, DexAnnotationAble daa) {
ByteBuffer in = annotationItemIn;
in.position(annotation_off);
int visibility = 0xFF & in.get();
DexAnnotationNode annotation = read_encoded_annotation(in);
annotation.visibility = Visibility.values()[visibility];
annotation.accept(daa);
}
private DexAnnotationNode read_encoded_annotation(ByteBuffer in) {
int type_idx = readULeb128i(in);
int size = readULeb128i(in);
String _typeString = getType(type_idx);
DexAnnotationNode ann = new DexAnnotationNode(_typeString, Visibility.RUNTIME);
for (int i = 0; i < size; i++) {
int name_idx = readULeb128i(in);
String nameString = getString(name_idx);
Object value = readEncodedValue(in);
ann.items.add(new DexAnnotationNode.Item(nameString, value));
}
return ann;
}
private Field getField(int id) {
fieldIdIn.position(id * 8);
int owner_idx = 0xFFFF & fieldIdIn.getShort();
int type_idx = 0xFFFF & fieldIdIn.getShort();
int name_idx = fieldIdIn.getInt();
return new Field(getType(owner_idx), getString(name_idx), getType(type_idx));
}
private String[] getTypeList(int offset) {
if (offset == 0) {
return new String[0];
}
typeListIn.position(offset);
int size = typeListIn.getInt();
String[] types = new String[size];
for (int i = 0; i < size; i++) {
types[i] = getType(0xFFFF & typeListIn.getShort());
}
return types;
}
private Proto getProto(int proto_idx) {
String[] parameterTypes;
String returnType;
protoIdIn.position(proto_idx * 12 + 4); // move to position and skip shorty_idx
int return_type_idx = protoIdIn.getInt();
int parameters_off = protoIdIn.getInt();
returnType = getType(return_type_idx);
parameterTypes = getTypeList(parameters_off);
return new Proto(parameterTypes, returnType);
}
private Method getMethod(int id) {
methoIdIn.position(id * 8);
int owner_idx = 0xFFFF & methoIdIn.getShort();
int proto_idx = 0xFFFF & methoIdIn.getShort();
int name_idx = methoIdIn.getInt();
return new Method(getType(owner_idx), getString(name_idx), getProto(proto_idx));
}
private String getString(int id) {
if (id == -1) {
return null;
}
int offset = stringIdIn.getInt(id * 4);
stringDataIn.position(offset);
int length = readULeb128i(stringDataIn);
try {
StringBuilder buff = new StringBuilder((int) (length * 1.5));
return Mutf8.decode(stringDataIn, buff);
} catch (UTFDataFormatException e) {
throw new DexException(e, "fail to load string %d@%08x", id, offset);
}
}
private String getType(int id) {
if (id == -1) {
return null;
}
return getString(typeIdIn.getInt(id * 4));
}
private int acceptField(ByteBuffer in, int lastIndex, DexClassVisitor dcv,
Map fieldAnnotationPositions, Object value, int config) {
int diff = (int) readULeb128i(in);
int field_access_flags = (int) readULeb128i(in);
int field_id = lastIndex + diff;
Field field = getField(field_id);
// //////////////////////////////////////////////////////////////
DexFieldVisitor dfv = dcv.visitField(field_access_flags, field, value);
if (dfv != null) {
if ((config & SKIP_ANNOTATION) == 0) {
Integer annotation_offset = fieldAnnotationPositions.get(field_id);
if (annotation_offset != null) {
try {
read_annotation_set_item(annotation_offset, dfv);
} catch (Exception e) {
throw new DexException(e, "while accept annotation in field:%s.", field.toString());
}
}
}
dfv.visitEnd();
}
// //////////////////////////////////////////////////////////////
return field_id;
}
private int acceptMethod(ByteBuffer in, int lastIndex, DexClassVisitor cv, Map methodAnnos,
Map parameterAnnos, int config, boolean firstMethod) {
int offset = in.position();
int diff = (int) readULeb128i(in);
int method_access_flags = (int) readULeb128i(in);
int code_off = (int) readULeb128i(in);
int method_id = lastIndex + diff;
Method method = getMethod(method_id);
// issue 200, methods may have same signature, we only need to keep the first one
if (!firstMethod && diff == 0) { // detect a duplicated method
WARN("GLITCH: duplicated method %s @%08x", method.toString(), offset);
if ((config & KEEP_ALL_METHODS) == 0) {
WARN("WARN: skip method %s @%08x", method.toString(), offset);
return method_id;
}
}
// issue 195, a or but not marked as ACC_CONSTRUCTOR,
if (0 == (method_access_flags & DexConstants.ACC_CONSTRUCTOR)
&& (method.getName().equals("") || method.getName().equals(""))) {
WARN("GLITCH: method %s @%08x not marked as ACC_CONSTRUCTOR", method.toString(), offset);
}
try {
DexMethodVisitor dmv = cv.visitMethod(method_access_flags, method);
if (dmv != null) {
if ((config & SKIP_ANNOTATION) == 0) {
Integer annotation_offset = methodAnnos.get(method_id);
if (annotation_offset != null) {
try {
read_annotation_set_item(annotation_offset, dmv);
} catch (Exception e) {
throw new DexException(e, "while accept annotation in method:%s.", method.toString());
}
}
Integer parameter_annotation_offset = parameterAnnos.get(method_id);
if (parameter_annotation_offset != null) {
try {
read_annotation_set_ref_list(parameter_annotation_offset, dmv);
} catch (Exception e) {
throw new DexException(e, "while accept parameter annotation in method:%s.",
method.toString());
}
}
}
if (code_off != 0) {
boolean keep = true;
if (0 != (SKIP_CODE & config)) {
keep = 0 != (KEEP_CLINIT & config) && method.getName().equals("");
}
if(keep) {
DexCodeVisitor dcv = dmv.visitCode();
if (dcv != null) {
try {
acceptCode(code_off, dcv, config, (method_access_flags & DexConstants.ACC_STATIC) != 0,
method);
} catch (Exception e) {
throw new DexException(e, "while accept code in method:[%s] @%08x", method.toString(),
code_off);
}
}
}
}
dmv.visitEnd();
}
} catch (Exception e) {
throw new DexException(e, "while accept method:[%s]", method.toString());
}
return method_id;
}
private void read_annotation_set_ref_list(int parameter_annotation_offset, DexMethodVisitor dmv) {
ByteBuffer in = annotationSetRefListIn;
in.position(parameter_annotation_offset);
int size = in.getInt();
for (int j = 0; j < size; j++) {
int param_annotation_offset = in.getInt();
if (param_annotation_offset == 0) {
continue;
}
DexAnnotationAble dpav = dmv.visitParameterAnnotation(j);
try {
if (dpav != null) {
read_annotation_set_item(param_annotation_offset, dpav);
}
} catch (Exception e) {
throw new DexException(e, "while accept parameter annotation in parameter:[%d]", j);
}
}
}
/**
* the size of class in dex file
*
* @return class_defs_size
*/
public final int getClassSize() {
return class_defs_size;
}
static class BadOpException extends RuntimeException{
public BadOpException(String fmt,Object ...args){
super(String.format(fmt,args));
}
}
private void findLabels(byte[] insns, BitSet nextBit, BitSet badOps, Map labelsMap, Set handlers,
Method method) {
Queue q = new LinkedList();
q.add(0);
q.addAll(handlers);
handlers.clear();
while (!q.isEmpty()) {
int offset = q.poll();
if (nextBit.get(offset)) {
continue;
} else {
nextBit.set(offset);
}
try {
travelInsn(labelsMap, q, insns, offset);
} catch (IndexOutOfBoundsException indexOutOfRange){
badOps.set(offset);
WARN("GLITCH: %04x %s | not enough space for reading instruction", offset, method.toString());
} catch (BadOpException badOp){
badOps.set(offset);
WARN("GLITCH: %04x %s | %s", offset, method.toString(), badOp.getMessage());
}
}
}
private void travelInsn(Map labelsMap, Queue q, byte[] insns, int offset) {
int u1offset = offset * 2;
if (u1offset >= insns.length) {
throw new IndexOutOfBoundsException();
}
int opcode = 0xFF & insns[u1offset];
Op op = null;
if (opcode < Op.ops.length) {
op = Op.ops[opcode];
}
if (op == null || op.format == null) {
throw new BadOpException("zero-width instruction op=0x%02x", opcode);
}
int target;
boolean canContinue = true;
if (op.canBranch()) {
switch (op.format) {
case kFmt10t:
target = offset + insns[u1offset + 1];
if (target < 0 || target * 2 > insns.length ) {
throw new BadOpException("jump out of insns %s -> %04x", op, target);
}
q.add(target);
order(labelsMap, target);
break;
case kFmt20t:
case kFmt21t:
target = offset + sshort(insns, u1offset + 2);
if (target < 0 || target * 2 > insns.length ) {
throw new BadOpException("jump out of insns %s -> %04x", op, target);
}
q.add(target);
order(labelsMap, target);
break;
case kFmt22t:
target = offset + sshort(insns, u1offset + 2);
int u = ubyte(insns, u1offset + 1);
boolean cmpSameReg = (u & 0x0F) == ((u >> 4) & 0x0F);
boolean skipTarget = false;
if (cmpSameReg) {
switch (op) {
case IF_EQ:
case IF_GE:
case IF_LE:
// means always jump, equals to goto
canContinue = false;
break;
case IF_NE:
case IF_GT:
case IF_LT:
// means always not jump
skipTarget = true;
break;
default:
break;
}
}
if (!skipTarget) {
if (target < 0 || target * 2 > insns.length ) {
throw new BadOpException("jump out of insns %s -> %04x", op, target);
}
q.add(target);
order(labelsMap, target);
}
break;
case kFmt30t:
case kFmt31t:
target = offset + sint(insns, u1offset + 2);
if (target < 0 || target * 2 > insns.length ) {
throw new BadOpException("jump out of insns %s -> %04x", op, target);
}
q.add(target);
order(labelsMap, target);
break;
default:
break;
}
}
if (op.canSwitch()) {
order(labelsMap, offset + op.format.size);// default
int u1SwitchData = 2 * (offset + sint(insns, u1offset + 2));
if (u1SwitchData + 2 < insns.length) {
switch (insns[u1SwitchData + 1]) {
case 0x01: // packed-switch-data
{
int size = ushort(insns, u1SwitchData + 2);
int b = u1SwitchData + 8;// targets
for (int i = 0; i < size; i++) {
target = offset + sint(insns, b + i * 4);
if (target < 0 || target * 2 > insns.length ) {
throw new BadOpException("jump out of insns %s -> %04x", op, target);
}
q.add(target);
order(labelsMap, target);
}
break;
}
case 0x02:// sparse-switch-data
{
int size = ushort(insns, u1SwitchData + 2);
int b = u1SwitchData + 4 + 4 * size;// targets
for (int i = 0; i < size; i++) {
target = offset + sint(insns, b + i * 4);
if (target < 0 || target * 2 > insns.length ) {
throw new BadOpException("jump out of insns %s -> %04x", op, target);
}
q.add(target);
order(labelsMap, target);
}
break;
}
default:
throw new BadOpException("bad payload for %s", op);
}
} else {
throw new BadOpException("bad payload offset for %s", op);
}
}
if (canContinue) {
int idx = Integer.MAX_VALUE;
switch (op.indexType) {
case kIndexStringRef:
if (op.format == InstructionFormat.kFmt31c) {
idx = uint(insns, u1offset + 2);
} else {// other
idx = ushort(insns, u1offset + 2);
}
canContinue = idx >= 0 && idx < string_ids_size;
break;
case kIndexTypeRef:
idx = ushort(insns, u1offset + 2);
canContinue = idx < type_ids_size;
break;
case kIndexMethodRef:
idx = ushort(insns, u1offset + 2);
canContinue = idx < method_ids_size;
break;
case kIndexFieldRef:
idx = ushort(insns, u1offset + 2);
canContinue = idx < field_ids_size;
break;
case kIndexCallSiteRef:
idx = ushort(insns, u1offset + 2);
canContinue = idx < call_site_ids_size;
break;
case kIndexMethodAndProtoRef:
idx = ushort(insns, u1offset + 2);
int idx2 = ushort(insns, u1offset + 6);
canContinue = idx < method_ids_size && idx2 < proto_ids_size;
break;
default:
}
if (!canContinue) {
throw new BadOpException("index-out-of-range for %s index: %d", op, idx);
}
}
if (canContinue && op.canContinue()) {
if (op == Op.NOP) {
switch (insns[u1offset + 1]) {
case 0x00:
q.add(offset + op.format.size);
break;
case 0x01: {
int size = ushort(insns, u1offset + 2);
q.add(offset + (size * 2) + 4);
break;
}
case 0x02: {
int size = ushort(insns, u1offset + 2);
q.add(offset + (size * 4) + 2);
break;
}
case 0x03: {
int element_width = ushort(insns, u1offset + 2);
int size = uint(insns, u1offset + 4);
q.add(offset + (size * element_width + 1) / 2 + 4);
break;
}
}
} else {
q.add(offset + op.format.size);
}
}
}
private void findTryCatch(ByteBuffer in, DexCodeVisitor dcv, int tries_size, int insn_size,
Map labelsMap, Set handlers) {
int encoded_catch_handler_list = in.position() + tries_size * 8;
ByteBuffer handlerIn = in.duplicate().order(ByteOrder.LITTLE_ENDIAN);
for (int i = 0; i < tries_size; i++) { // try_item
int start_addr = in.getInt();
int insn_count = 0xFFFF & in.getShort();
int handler_offset = 0xFFFF & in.getShort();
if (start_addr > insn_size) {
continue;
}
order(labelsMap, start_addr);
int end = start_addr + insn_count;
order(labelsMap, end);
handlerIn.position(encoded_catch_handler_list + handler_offset);// move to encoded_catch_handler
boolean catchAll = false;
int listSize = (int) readLeb128i(handlerIn);
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) readULeb128i(handlerIn);
int handler = (int) readULeb128i(handlerIn);
order(labelsMap, handler);
handlers.add(handler);
types[k] = getType(type_id);
labels[k] = labelsMap.get(handler);
}
if (catchAll) {
int handler = (int) readULeb128i(handlerIn);
order(labelsMap, handler);
handlers.add(handler);
labels[listSize] = labelsMap.get(handler);
}
dcv.visitTryCatch(labelsMap.get(start_addr), labelsMap.get(end), labels, types);
}
}
/* package */void acceptCode(int code_off, DexCodeVisitor dcv, int config, boolean isStatic, Method method) {
ByteBuffer in = codeItemIn;
in.position(code_off);
int registers_size = 0xFFFF & in.getShort();
in.getShort();// ins_size ushort
in.getShort();// outs_size ushort
int tries_size = 0xFFFF & in.getShort();
int debug_info_off = in.getInt();
int insns = in.getInt();
byte[] insnsArray = new byte[insns * 2];
in.get(insnsArray);
dcv.visitRegister(registers_size);
BitSet nextInsn = new BitSet();
Map labelsMap = new TreeMap();
Set handlers = new HashSet();
// 处理异常处理
if (tries_size > 0) {
if ((insns & 0x01) != 0) {// skip padding
in.getShort();
}
if (0 == (config & SKIP_EXCEPTION)) {
findTryCatch(in, dcv, tries_size, insns, labelsMap, handlers);
}
}
// 处理debug信息
if (debug_info_off != 0 && (0 == (config & SKIP_DEBUG))) {
DexDebugVisitor ddv = dcv.visitDebug();
if (ddv != null) {
read_debug_info(debug_info_off, registers_size, isStatic, method, labelsMap, ddv);
ddv.visitEnd();
}
}
BitSet badOps = new BitSet();
findLabels(insnsArray, nextInsn, badOps, labelsMap, handlers, method);
acceptInsn(insnsArray, dcv, nextInsn, badOps, labelsMap);
dcv.visitEnd();
}
// 处理指令
private void acceptInsn(byte[] insns, DexCodeVisitor dcv, BitSet nextInsn, BitSet badOps, Map labelsMap) {
Iterator labelOffsetIterator = labelsMap.keySet().iterator();
Integer nextLabelOffset = labelOffsetIterator.hasNext() ? labelOffsetIterator.next() : null;
Op[] values = Op.ops;
for (int offset = nextInsn.nextSetBit(0); offset >= 0; offset = nextInsn.nextSetBit(offset + 1)) {
// issue 65, a label may `inside` an instruction
// visit all label with offset <= currentOffset
while (nextLabelOffset != null) {
if (nextLabelOffset <= offset) {
dcv.visitLabel(labelsMap.get(nextLabelOffset));
nextLabelOffset = labelOffsetIterator.hasNext() ? labelOffsetIterator.next() : null;
} else {
// the label is after this instruction
break;
}
}
if(badOps.get(offset)){
dcv.visitStmt0R(Op.BAD_OP);
continue;
}
int u1offset = offset * 2;
int opcode = 0xFF & insns[u1offset];
Op op = values[opcode];
int a, b, c, target;
switch (op.format) {
// case kFmt00x: break;
case kFmt10x:
dcv.visitStmt0R(op);
break;
case kFmt11x:
dcv.visitStmt1R(op, 0xFF & insns[u1offset + 1]);
break;
case kFmt12x:
a = ubyte(insns, u1offset + 1);
dcv.visitStmt2R(op, a & 0xF, a >> 4);
break;
// case kFmt20bc:break;
case kFmt10t:
target = offset + insns[u1offset + 1];
dcv.visitJumpStmt(op, -1, -1, labelsMap.get(target));
break;
case kFmt20t:
target = offset + sshort(insns, u1offset + 2);
dcv.visitJumpStmt(op, -1, -1, labelsMap.get(target));
break;
case kFmt21t:
target = offset + sshort(insns, u1offset + 2);
dcv.visitJumpStmt(op, ubyte(insns, u1offset + 1), -1, labelsMap.get(target));
break;
case kFmt22t:
target = offset + sshort(insns, u1offset + 2);
a = ubyte(insns, u1offset + 1);
b = a & 0x0F;
c = a >> 4;
boolean ignore = false;
if (b == c) {
switch (op) {
case IF_EQ:
case IF_GE:
case IF_LE:
// means always jump, equals to goto
dcv.visitJumpStmt(Op.GOTO, 0, 0, labelsMap.get(target));
ignore = true;
break;
case IF_NE:
case IF_GT:
case IF_LT:
// means always not jump
ignore = true;
break;
default:
break;
}
}
if (!ignore) {
dcv.visitJumpStmt(op, b, c, labelsMap.get(target));
}
break;
case kFmt30t:
target = offset + sint(insns, u1offset + 2);
dcv.visitJumpStmt(op, -1, -1, labelsMap.get(target));
break;
case kFmt31t:
target = offset + sint(insns, u1offset + 2);
a = ubyte(insns, u1offset + 1);
int u1SwitchData = 2 * target;
if (op == Op.FILL_ARRAY_DATA) {
int element_width = ushort(insns, u1SwitchData + 2);
int size = uint(insns, u1SwitchData + 4);
switch (element_width) {
case 1: {
byte[] data = new byte[size];
System.arraycopy(insns, u1SwitchData + 8, data, 0, size);
dcv.visitFillArrayDataStmt(op, a, data);
}
break;
case 2: {
short[] data = new short[size];
for (int i = 0; i < size; i++) {
data[i] = (short) sshort(insns, u1SwitchData + 8 + 2 * i);
}
dcv.visitFillArrayDataStmt(op, a, data);
}
break;
case 4: {
int[] data = new int[size];
for (int i = 0; i < size; i++) {
data[i] = sint(insns, u1SwitchData + 8 + 4 * i);
}
dcv.visitFillArrayDataStmt(op, a, data);
}
break;
case 8: {
long[] data = new long[size];
for (int i = 0; i < size; i++) {
int t = u1SwitchData + 8 + 8 * i;
long z = 0;
z |= ((long) ushort(insns, t + 0)) << 0;
z |= ((long) ushort(insns, t + 2)) << 16;
z |= ((long) ushort(insns, t + 4)) << 32;
z |= ((long) ushort(insns, t + 6)) << 48;
data[i] = z;
}
dcv.visitFillArrayDataStmt(op, a, data);
}
break;
}
} else if (op == Op.SPARSE_SWITCH) {
int size = sshort(insns, u1SwitchData + 2);
int keys[] = new int[size];
DexLabel labels[] = new DexLabel[size];
int z = u1SwitchData + 4;
for (int i = 0; i < size; i++) {
keys[i] = sint(insns, z + i * 4);
}
z += size * 4;
for (int i = 0; i < size; i++) {
labels[i] = labelsMap.get(offset + sint(insns, z + i * 4));
}
dcv.visitSparseSwitchStmt(op, a, keys, labels);
} else {
int size = sshort(insns, u1SwitchData + 2);
int first_key = sint(insns, u1SwitchData + 4);
DexLabel labels[] = new DexLabel[size];
int z = u1SwitchData + 8;
for (int i = 0; i < size; i++) {
labels[i] = labelsMap.get(offset + sint(insns, z));
z += 4;
}
dcv.visitPackedSwitchStmt(op, a, first_key, labels);
}
break;
case kFmt21c:
a = ubyte(insns, u1offset + 1);
b = ushort(insns, u1offset + 2);
switch (op.indexType) {
case kIndexStringRef:
dcv.visitConstStmt(op, a, getString(b));
break;
case kIndexFieldRef:
dcv.visitFieldStmt(op, a, -1, getField(b));
break;
case kIndexTypeRef:
if (op == Op.CONST_CLASS) {
dcv.visitConstStmt(op, a, new DexType(getType(b)));
} else {
dcv.visitTypeStmt(op, a, -1, getType(b));
}
break;
default:
break;
}
break;
case kFmt22c:
a = ubyte(insns, u1offset + 1);
b = ushort(insns, u1offset + 2);
switch (op.indexType) {
case kIndexFieldRef:
dcv.visitFieldStmt(op, a & 0xF, a >> 4, getField(b));
break;
case kIndexTypeRef:
dcv.visitTypeStmt(op, a & 0xF, a >> 4, getType(b));
break;
default:
break;
}
break;
case kFmt31c:
if (op.indexType == InstructionIndexType.kIndexStringRef) {
a = ubyte(insns, u1offset + 1);
b = uint(insns, u1offset + 2);
dcv.visitConstStmt(op, a, getString(b));
}
break;
case kFmt35c: {
a = ubyte(insns, u1offset + 1);
b = ushort(insns, u1offset + 2);
int dc = ubyte(insns, u1offset + 4); // DC
int fe = ubyte(insns, u1offset + 5); // FE
int regs[] = new int[a >> 4];
switch (a >> 4) {
case 5:
regs[4] = a & 0xF;// G
case 4:
regs[3] = 0xF & (fe >> 4);// F
case 3:
regs[2] = 0xF & (fe >> 0);// E
case 2:
regs[1] = 0xF & (dc >> 4);// D
case 1:
regs[0] = 0xF & (dc >> 0);// C
}
if (op.indexType == InstructionIndexType.kIndexTypeRef) {
dcv.visitFilledNewArrayStmt(op, regs, getType(b));
} else if (op.indexType == InstructionIndexType.kIndexCallSiteRef) {
Object[] callsite = getCallSite(b);
Object[] constArgs = Arrays.copyOfRange(callsite, 3, callsite.length);
dcv.visitMethodStmt(op, regs, (String) callsite[1], (Proto) callsite[2], (MethodHandle) callsite[0], constArgs);
} else {
dcv.visitMethodStmt(op, regs, getMethod(b));
}
}
break;
case kFmt3rc: {
a = ubyte(insns, u1offset + 1);
b = ushort(insns, u1offset + 2);
c = ushort(insns, u1offset + 4);
int regs[] = new int[a];
for (int i = 0; i < a; i++) {
regs[i] = c + i;
}
if (op.indexType == InstructionIndexType.kIndexTypeRef) {
dcv.visitFilledNewArrayStmt(op, regs, getType(b));
} else if (op.indexType == InstructionIndexType.kIndexCallSiteRef) {
Object[] callsite = getCallSite(b);
Object[] constArgs = Arrays.copyOfRange(callsite, 3, callsite.length - 3);
dcv.visitMethodStmt(op, regs, (String) callsite[1], (Proto) callsite[2], (MethodHandle) callsite[0], constArgs);
} else {
dcv.visitMethodStmt(op, regs, getMethod(b));
}
}
break;
case kFmt45cc: {
a = ubyte(insns, u1offset + 1);
b = ushort(insns, u1offset + 2);
int dc = ubyte(insns, u1offset + 4); // DC
int fe = ubyte(insns, u1offset + 5); // FE
int h = ushort(insns, u1offset + 6);
int regs[] = new int[a >> 4];
switch (a >> 4) {
case 5:
regs[4] = a & 0xF;// G
case 4:
regs[3] = 0xF & (fe >> 4);// F
case 3:
regs[2] = 0xF & (fe >> 0);// E
case 2:
regs[1] = 0xF & (dc >> 4);// D
case 1:
regs[0] = 0xF & (dc >> 0);// C
}
dcv.visitMethodStmt(op, regs, getMethod(b), getProto(h));
}
break;
case kFmt4rcc: {
a = ubyte(insns, u1offset + 1);
b = ushort(insns, u1offset + 2);
c = ushort(insns, u1offset + 4);
int h = ushort(insns, u1offset + 6);
int regs[] = new int[a];
for (int i = 0; i < a; i++) {
regs[i] = c + i;
}
dcv.visitMethodStmt(op, regs, getMethod(b), getProto(h));
}
break;
case kFmt22x:
a = ubyte(insns, u1offset + 1);
b = ushort(insns, u1offset + 2);
dcv.visitStmt2R(op, a, b);
break;
case kFmt23x:
a = ubyte(insns, u1offset + 1);
b = ubyte(insns, u1offset + 2);
c = ubyte(insns, u1offset + 3);
dcv.visitStmt3R(op, a, b, c);
break;
case kFmt32x:
a = ushort(insns, u1offset + 2);
b = ushort(insns, u1offset + 4);
dcv.visitStmt2R(op, a, b);
break;
case kFmt11n:
a = insns[u1offset + 1];
dcv.visitConstStmt(op, a & 0xF, a >> 4);
break;
case kFmt21h:
a = ubyte(insns, u1offset + 1);
b = sshort(insns, u1offset + 2);
if (op == Op.CONST_HIGH16) {
dcv.visitConstStmt(op, a, b << 16);
} else {
dcv.visitConstStmt(op, a, ((long) b) << 48);
}
break;
case kFmt21s:
a = ubyte(insns, u1offset + 1);
b = sshort(insns, u1offset + 2);
if (op == Op.CONST_16) {
dcv.visitConstStmt(op, a, b);
} else {
dcv.visitConstStmt(op, a, (long) b);
}
break;
case kFmt22b:
a = ubyte(insns, u1offset + 1);
b = ubyte(insns, u1offset + 2);
c = sbyte(insns, u1offset + 3);
dcv.visitStmt2R1N(op, a, b, c);
break;
case kFmt22s:
a = ubyte(insns, u1offset + 1);
b = sshort(insns, u1offset + 2);
dcv.visitStmt2R1N(op, a & 0xF, a >> 4, b);
break;
// case kFmt22cs:break;
case kFmt31i:
a = ubyte(insns, u1offset + 1);
b = sint(insns, u1offset + 2);
if (op == Op.CONST) {
dcv.visitConstStmt(op, a, b);
} else {
dcv.visitConstStmt(op, a, (long) b);
}
break;
case kFmt51l:
a = ubyte(insns, u1offset + 1);
long z = 0;
z |= ((long) ushort(insns, u1offset + 2)) << 0;
z |= ((long) ushort(insns, u1offset + 4)) << 16;
z |= ((long) ushort(insns, u1offset + 6)) << 32;
z |= ((long) ushort(insns, u1offset + 8)) << 48;
dcv.visitConstStmt(op, a, z);
break;
}
}
while (nextLabelOffset != null) {
dcv.visitLabel(labelsMap.get(nextLabelOffset));
if (labelOffsetIterator.hasNext()) {
nextLabelOffset = labelOffsetIterator.next();
} else {
break;
}
}
}
private Object[] getCallSite(int b) {
callSiteIdIn.position(b * 4);
int call_site_off = callSiteIdIn.getInt();
return read_encoded_array_item(call_site_off);
}
/**
* An entry in the resulting locals table
*/
static private class LocalEntry {
public String name, type, signature;
private LocalEntry(String name, String type) {
this.name = name;
this.type = type;
}
private LocalEntry(String name, String type, String signature) {
this.name = name;
this.type = type;
this.signature = signature;
}
}
}