com.googlecode.d2j.reader.DexFileReader Maven / Gradle / Ivy
The newest version!
/*
* 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;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy