Finish DexParser

This commit is contained in:
LoveSy 2023-01-08 06:32:25 +08:00 committed by LoveSy
parent 8cec9fe29a
commit 7ac42ebe4d
6 changed files with 294 additions and 101 deletions

View File

@ -913,9 +913,8 @@ public class LSPosedContext extends XposedContext {
Log.e(TAG, mPackageName + ": " + message, throwable); Log.e(TAG, mPackageName + ": " + message, throwable);
} }
@Nullable
@Override @Override
public DexParser parseDex(ByteBuffer dexData) throws IOException { public DexParser parseDex(@NonNull ByteBuffer dexData, boolean includeAnnotations) throws IOException {
return new LSPosedDexParser(dexData); return new LSPosedDexParser(dexData, includeAnnotations);
} }
} }

View File

@ -13,15 +13,24 @@ import io.github.libxposed.utils.DexParser;
public class LSPosedDexParser implements DexParser { public class LSPosedDexParser implements DexParser {
long cookie; long cookie;
@NonNull
final ByteBuffer data; final ByteBuffer data;
@NonNull
final StringId[] strings; final StringId[] strings;
@NonNull
final TypeId[] typeIds; final TypeId[] typeIds;
@NonNull
final ProtoId[] protoIds; final ProtoId[] protoIds;
@NonNull
final FieldId[] fieldIds; final FieldId[] fieldIds;
@NonNull
final MethodId[] methodIds; final MethodId[] methodIds;
@NonNull
final Annotation[] annotations; final Annotation[] annotations;
@NonNull
final Array[] arrays;
public LSPosedDexParser(ByteBuffer buffer) throws IOException { public LSPosedDexParser(@NonNull ByteBuffer buffer, boolean includeAnnotations) throws IOException {
if (!buffer.isDirect() || !buffer.asReadOnlyBuffer().hasArray()) { if (!buffer.isDirect() || !buffer.asReadOnlyBuffer().hasArray()) {
data = ByteBuffer.allocateDirect(buffer.capacity()); data = ByteBuffer.allocateDirect(buffer.capacity());
data.put(buffer); data.put(buffer);
@ -29,7 +38,8 @@ public class LSPosedDexParser implements DexParser {
data = buffer; data = buffer;
} }
try { try {
long[] args = new long[1]; long[] args = new long[2];
args[1] = includeAnnotations ? 1 : 0;
var out = (Object[]) DexParserBridge.openDex(buffer, args); var out = (Object[]) DexParserBridge.openDex(buffer, args);
cookie = args[0]; cookie = args[0];
// out[0]: String[] // out[0]: String[]
@ -37,7 +47,9 @@ public class LSPosedDexParser implements DexParser {
// out[2]: int[][] // out[2]: int[][]
// out[3]: int[] // out[3]: int[]
// out[4]: int[] // out[4]: int[]
// out[5]: int[]
// out[6]: Object[] // out[6]: Object[]
// out[7]: Object[]
var strings = (Object[]) out[0]; var strings = (Object[]) out[0];
this.strings = new StringId[strings.length]; this.strings = new StringId[strings.length];
for (int i = 0; i < strings.length; ++i) { for (int i = 0; i < strings.length; ++i) {
@ -78,6 +90,15 @@ public class LSPosedDexParser implements DexParser {
} else { } else {
this.annotations = new Annotation[0]; this.annotations = new Annotation[0];
} }
if (out[7] != null) {
var b = (Object[]) out[7];
this.arrays = new Array[b.length / 2];
for (int i = 0; i < this.arrays.length; ++i) {
this.arrays[i] = new LSPosedArray((int[]) b[2 * i], (Object[]) b[2 * i + 1]);
}
} else {
this.arrays = new Array[0];
}
} catch (Throwable e) { } catch (Throwable e) {
throw new IOException("Invalid dex file", e); throw new IOException("Invalid dex file", e);
} }
@ -245,19 +266,37 @@ public class LSPosedDexParser implements DexParser {
} }
} }
static class LSPosedArray implements Array {
@NonNull
final Value[] values;
LSPosedArray(int[] elements, @NonNull Object[] values) {
this.values = new Value[values.length];
for (int i = 0; i < values.length; ++i) {
this.values[i] = new LSPosedValue(elements[i], (ByteBuffer) values[i]);
}
}
@NonNull
@Override
public Value[] getValues() {
return values;
}
}
class LSPosedAnnotation implements Annotation { class LSPosedAnnotation implements Annotation {
int visibility; int visibility;
@NonNull @NonNull
final TypeId type; final TypeId type;
@Nullable @NonNull
final AnnotationElement[] elements; final Element[] elements;
LSPosedAnnotation(int visibility, int type, @NonNull int[] elements, @NonNull Object[] elementValues) { LSPosedAnnotation(int visibility, int type, @NonNull int[] elements, @NonNull Object[] elementValues) {
this.visibility = visibility; this.visibility = visibility;
this.type = typeIds[type]; this.type = typeIds[type];
this.elements = new AnnotationElement[elementValues.length]; this.elements = new Element[elementValues.length];
for (int i = 0; i < elementValues.length; ++i) { for (int i = 0; i < elementValues.length; ++i) {
this.elements[i] = new LSPosedAnnotationElement(elements[i * 2], elements[i * 2 + 1], (ByteBuffer) elementValues[i]); this.elements[i] = new LSPosedElement(elements[i * 2], elements[i * 2 + 1], (ByteBuffer) elementValues[i]);
} }
} }
@ -272,22 +311,19 @@ public class LSPosedDexParser implements DexParser {
return type; return type;
} }
@Nullable @NonNull
@Override @Override
public AnnotationElement[] getElements() { public Element[] getElements() {
return elements; return elements;
} }
} }
class LSPosedAnnotationElement implements AnnotationElement { static class LSPosedValue implements Value {
@NonNull
final StringId name;
final int valueType; final int valueType;
@Nullable @Nullable
final byte[] value; final byte[] value;
LSPosedAnnotationElement(int name, int valueType, @Nullable ByteBuffer value) { LSPosedValue(int valueType, @Nullable ByteBuffer value) {
this.name = strings[name];
this.valueType = valueType; this.valueType = valueType;
if (value != null) { if (value != null) {
this.value = new byte[value.remaining()]; this.value = new byte[value.remaining()];
@ -297,21 +333,31 @@ public class LSPosedDexParser implements DexParser {
} }
} }
@NonNull @Nullable
@Override @Override
public StringId getName() { public byte[] getValue() {
return name; return value;
} }
@Override @Override
public int getValueType() { public int getValueType() {
return valueType; return valueType;
} }
}
@Nullable class LSPosedElement extends LSPosedValue implements Element {
@NonNull
final StringId name;
LSPosedElement(int name, int valueType, @Nullable ByteBuffer value) {
super(valueType, value);
this.name = strings[name];
}
@NonNull
@Override @Override
public byte[] getValue() { public StringId getName() {
return value; return name;
} }
} }
@ -351,6 +397,12 @@ public class LSPosedDexParser implements DexParser {
return annotations; return annotations;
} }
@NonNull
@Override
public Array[] getArrays() {
return arrays;
}
@Override @Override
synchronized public void visitDefinedClasses(@NonNull ClassVisitor visitor) { synchronized public void visitDefinedClasses(@NonNull ClassVisitor visitor) {
if (cookie == 0) { if (cookie == 0) {

View File

@ -25,61 +25,13 @@
#include <absl/container/flat_hash_map.h> #include <absl/container/flat_hash_map.h>
namespace { namespace {
using AnnotationList = std::vector<std::tuple<jint/*vis*/, jint /*type*/, std::vector<std::tuple<jint /*name*/, jint/*value_type*/, std::string_view/*value*/>>>>; using Value = std::tuple<jint /*type*/, std::vector<jbyte> /*data*/>;
using Array = std::vector<Value>;
void ParseAnnotation(JNIEnv *env, dex::Reader &dex, AnnotationList &annotation_list, using ArrayList = std::list<Array>;
std::vector<jint> indices, using Element = std::tuple<jint /*name*/, Value>;
const dex::AnnotationSetItem *annotation) { using ElementList = std::vector<Element>;
if (annotation == nullptr) { using Annotation = std::tuple<jint/*vis*/, jint /*type*/, ElementList>;
return; using AnnotationList = std::vector<Annotation>;
}
for (size_t i = 0; i < annotation->size; ++i) {
auto *item = dex.dataPtr<dex::AnnotationItem>(annotation->entries[i]);
auto *annotation_data = item->annotation;
indices.emplace_back(annotation_list.size());
auto &[visibility, type, items] = annotation_list.emplace_back(item->visibility,
dex::ReadULeb128(
&annotation_data),
std::vector<std::tuple<jint, jint, std::string_view>>());
auto size = dex::ReadULeb128(&annotation_data);
items.resize(size);
for (size_t j = 0; j < size; ++j) {
auto &[name, value_type, value] = items[j];
name = static_cast<jint>(dex::ReadULeb128(&annotation_data));
auto arg_and_type = *annotation_data++;
value_type = arg_and_type & 0x1f;
auto value_arg = arg_and_type >> 5;
switch (value_type) {
case 0x00: // byte
case 0x1f: // boolean
value = {reinterpret_cast<char *>(const_cast<dex::u1 *>(annotation_data)),
1};
break;
case 0x02: // short
case 0x03: // char
case 0x04: // int
case 0x06: // long
case 0x10: // float
case 0x11: // double
case 0x17: // string
case 0x18: // type
case 0x19: // field
case 0x1a: // method
case 0x1b: // enum
value = {
reinterpret_cast<char *>(const_cast<dex::u1 *>(annotation_data)),
static_cast<size_t>(value_arg)};
break;
case 0x1c: // array
case 0x1d: // annotation
case 0x1e: // null
default:
// not supported
break;
}
}
}
}
class DexParser : public dex::Reader { class DexParser : public dex::Reader {
public: public:
@ -116,6 +68,145 @@ namespace {
absl::flat_hash_map<jint, MethodBody> method_bodies; absl::flat_hash_map<jint, MethodBody> method_bodies;
}; };
template<class T>
static std::vector<jbyte> ParseIntValue(const dex::u1 **pptr, size_t size) {
static_assert(std::is_integral<T>::value, "must be an integral type");
std::vector<jbyte> ret(sizeof(T));
T &value = *reinterpret_cast<T *>(ret.data());
for (size_t i = 0; i < size; ++i) {
value |= T(*(*pptr)++) << (i * 8);
}
// sign-extend?
if constexpr (std::is_signed_v<T>) {
size_t shift = (sizeof(T) - size) * 8;
value = T(value << shift) >> shift;
}
return ret;
}
template<class T>
static std::vector<jbyte> ParseFloatValue(const dex::u1 **pptr, size_t size) {
std::vector<jbyte> ret(sizeof(T));
T &value = *reinterpret_cast<T *>(ret.data());
int start_byte = sizeof(T) - size;
for (dex::u1 *p = reinterpret_cast<dex::u1 *>(&value) + start_byte; size > 0;
--size) {
*p++ = *(*pptr)++;
}
return ret;
}
Annotation
ParseAnnotation(const dex::u1 **annotation, AnnotationList &annotation_list,
ArrayList &array_list);
Array
ParseArray(const dex::u1 **array, AnnotationList &annotation_list, ArrayList &array_list);
Value
ParseValue(const dex::u1 **value, AnnotationList &annotation_list, ArrayList &array_list) {
Value res;
auto &[type, value_content] = res;
auto header = *(*value)++;
type = header & dex::kEncodedValueTypeMask;
dex::u1 arg = header >> dex::kEncodedValueArgShift;
switch (type) {
case dex::kEncodedByte:
value_content = ParseIntValue<int8_t>(value, arg + 1);
break;
case dex::kEncodedShort:
value_content = ParseIntValue<int16_t>(value, arg + 1);
break;
case dex::kEncodedChar:
value_content = ParseIntValue<uint16_t>(value, arg + 1);
break;
case dex::kEncodedInt:
value_content = ParseIntValue<int32_t>(value, arg + 1);
break;
case dex::kEncodedLong:
value_content = ParseIntValue<int64_t>(value, arg + 1);
break;
case dex::kEncodedFloat:
value_content = ParseFloatValue<float>(value, arg + 1);
break;
case dex::kEncodedDouble:
value_content = ParseFloatValue<double>(value, arg + 1);
break;
case dex::kEncodedMethodType:
case dex::kEncodedMethodHandle:
case dex::kEncodedString:
case dex::kEncodedType:
case dex::kEncodedField:
case dex::kEncodedMethod:
case dex::kEncodedEnum:
value_content = ParseIntValue<uint32_t>(value, arg + 1);
break;
case dex::kEncodedArray:
value_content.resize(sizeof(jint));
*reinterpret_cast<jint *>(value_content.data()) = static_cast<jint>(array_list.size());
array_list.emplace_back(ParseArray(value, annotation_list, array_list));
break;
case dex::kEncodedAnnotation:
value_content.resize(sizeof(jint));
*reinterpret_cast<jint *>(value_content.data()) = static_cast<jint>(annotation_list.size());
annotation_list.emplace_back(ParseAnnotation(value, annotation_list, array_list));
break;
case dex::kEncodedNull:
break;
case dex::kEncodedBoolean:
value_content = {static_cast<jbyte>(arg == 1)};
break;
default:
__builtin_unreachable();
}
return res;
}
Annotation
ParseAnnotation(const dex::u1 **annotation, AnnotationList &annotation_list,
ArrayList &array_list) {
Annotation ret = {dex::kVisibilityEncoded,
dex::ReadULeb128(annotation),
ElementList{}};
auto &[vis, type, element_list] = ret;
auto size = dex::ReadULeb128(annotation);
element_list.resize(size);
for (size_t j = 0; j < size; ++j) {
auto &[name, value] = element_list[j];
name = static_cast<jint>(dex::ReadULeb128(annotation));
value = ParseValue(annotation, annotation_list, array_list);
}
return ret;
}
Array
ParseArray(const dex::u1 **array, AnnotationList &annotation_list, ArrayList &array_list) {
auto size = dex::ReadULeb128(array);
Array ret;
ret.reserve(size);
for (size_t i = 0; i < size; ++i) {
ret.emplace_back(ParseValue(array, annotation_list, array_list));
}
return ret;
}
void ParseAnnotationSet(dex::Reader &dex, AnnotationList &annotation_list,
ArrayList &array_list, std::vector<jint> &indices,
const dex::AnnotationSetItem *annotation_set) {
if (annotation_set == nullptr) {
return;
}
for (size_t i = 0; i < annotation_set->size; ++i) {
auto *item = dex.dataPtr<dex::AnnotationItem>(annotation_set->entries[i]);
auto *annotation_data = item->annotation;
indices.emplace_back(annotation_list.size());
auto &[visibility, type, element_list] = annotation_list.emplace_back(
ParseAnnotation(&annotation_data, annotation_list, array_list));
visibility = item->visibility;
}
}
} }
namespace lspd { namespace lspd {
@ -128,6 +219,9 @@ namespace lspd {
auto *dex_data = env->GetDirectBufferAddress(data); auto *dex_data = env->GetDirectBufferAddress(data);
auto *dex_reader = new DexParser(reinterpret_cast<dex::u1 *>(dex_data), dex_size); auto *dex_reader = new DexParser(reinterpret_cast<dex::u1 *>(dex_data), dex_size);
auto *args_ptr = env->GetLongArrayElements(args, nullptr);
auto include_annotations = args_ptr[1];
env->ReleaseLongArrayElements(args, args_ptr, JNI_ABORT);
env->SetLongArrayRegion(args, 0, 1, reinterpret_cast<const jlong *>(&dex_reader)); env->SetLongArrayRegion(args, 0, 1, reinterpret_cast<const jlong *>(&dex_reader));
auto &dex = *dex_reader; auto &dex = *dex_reader;
if (dex.IsCompact()) { if (dex.IsCompact()) {
@ -137,7 +231,7 @@ namespace lspd {
auto object_class = env->FindClass("java/lang/Object"); auto object_class = env->FindClass("java/lang/Object");
auto string_class = env->FindClass("java/lang/String"); auto string_class = env->FindClass("java/lang/String");
auto int_array_class = env->FindClass("[I"); auto int_array_class = env->FindClass("[I");
auto out = env->NewObjectArray(7, object_class, nullptr); auto out = env->NewObjectArray(8, object_class, nullptr);
auto out0 = env->NewObjectArray(static_cast<jint>(dex.StringIds().size()), auto out0 = env->NewObjectArray(static_cast<jint>(dex.StringIds().size()),
string_class, nullptr); string_class, nullptr);
auto strings = dex.StringIds(); auto strings = dex.StringIds();
@ -213,6 +307,7 @@ namespace lspd {
dex.class_data.resize(classes.size()); dex.class_data.resize(classes.size());
AnnotationList annotation_list; AnnotationList annotation_list;
ArrayList array_list;
for (size_t i = 0; i < classes.size(); ++i) { for (size_t i = 0; i < classes.size(); ++i) {
auto &class_def = classes[i]; auto &class_def = classes[i];
@ -304,7 +399,9 @@ namespace lspd {
} }
} }
ParseAnnotation(env, dex, annotation_list, class_data.annotations, class_annotation); if (!include_annotations) continue;
ParseAnnotationSet(dex, annotation_list, array_list, class_data.annotations,
class_annotation);
auto *field_annotations = annotations auto *field_annotations = annotations
? reinterpret_cast<const dex::FieldAnnotationsItem *>( ? reinterpret_cast<const dex::FieldAnnotationsItem *>(
@ -312,9 +409,9 @@ namespace lspd {
for (size_t k = 0; k < field_annotations_count; ++k) { for (size_t k = 0; k < field_annotations_count; ++k) {
auto *field_annotation = dex.dataPtr<dex::AnnotationSetItem>( auto *field_annotation = dex.dataPtr<dex::AnnotationSetItem>(
field_annotations[k].annotations_off); field_annotations[k].annotations_off);
ParseAnnotation(env, dex, annotation_list, ParseAnnotationSet(dex, annotation_list, array_list,
dex.field_annotations[static_cast<jint>(field_annotations[k].field_idx)], dex.field_annotations[static_cast<jint>(field_annotations[k].field_idx)],
field_annotation); field_annotation);
} }
auto *method_annotations = field_annotations auto *method_annotations = field_annotations
@ -324,9 +421,9 @@ namespace lspd {
for (size_t k = 0; k < method_annotations_count; ++k) { for (size_t k = 0; k < method_annotations_count; ++k) {
auto *method_annotation = dex.dataPtr<dex::AnnotationSetItem>( auto *method_annotation = dex.dataPtr<dex::AnnotationSetItem>(
method_annotations[k].annotations_off); method_annotations[k].annotations_off);
ParseAnnotation(env, dex, annotation_list, ParseAnnotationSet(dex, annotation_list, array_list,
dex.method_annotations[static_cast<jint>(method_annotations[k].method_idx)], dex.method_annotations[static_cast<jint>(method_annotations[k].method_idx)],
method_annotation); method_annotation);
} }
auto *parameter_annotations = method_annotations auto *parameter_annotations = method_annotations
@ -341,14 +438,16 @@ namespace lspd {
if (parameter_annotation->list[l].annotations_off != 0) { if (parameter_annotation->list[l].annotations_off != 0) {
auto *parameter_annotation_item = dex.dataPtr<dex::AnnotationSetItem>( auto *parameter_annotation_item = dex.dataPtr<dex::AnnotationSetItem>(
parameter_annotation->list[l].annotations_off); parameter_annotation->list[l].annotations_off);
ParseAnnotation(env, dex, annotation_list, indices, ParseAnnotationSet(dex, annotation_list, array_list, indices,
parameter_annotation_item); parameter_annotation_item);
} }
indices.emplace_back(dex::kNoIndex); indices.emplace_back(dex::kNoIndex);
} }
} }
} }
return out;
if (!include_annotations) return out;
auto out5 = env->NewIntArray(static_cast<jint>(2 * annotation_list.size())); auto out5 = env->NewIntArray(static_cast<jint>(2 * annotation_list.size()));
auto out6 = env->NewObjectArray(static_cast<jint>(2 * annotation_list.size()), object_class, auto out6 = env->NewObjectArray(static_cast<jint>(2 * annotation_list.size()), object_class,
nullptr); nullptr);
@ -360,9 +459,10 @@ namespace lspd {
auto out6i1 = env->NewObjectArray(static_cast<jint>(items.size()), object_class, auto out6i1 = env->NewObjectArray(static_cast<jint>(items.size()), object_class,
nullptr); nullptr);
size_t j = 0; size_t j = 0;
for (auto&[name, value_type, value]: items) { for (auto&[name, value]: items) {
auto java_value = env->NewDirectByteBuffer(const_cast<char *>(value.data()), auto &[value_type, value_data] = value;
value.size()); auto java_value = value_data.empty() ? nullptr : env->NewDirectByteBuffer(
value_data.data(), value_data.size());
env->SetObjectArrayElement(out6i1, static_cast<jint>(j), java_value); env->SetObjectArrayElement(out6i1, static_cast<jint>(j), java_value);
out6i0_ptr[2 * j] = name; out6i0_ptr[2 * j] = name;
out6i0_ptr[2 * j + 1] = value_type; out6i0_ptr[2 * j + 1] = value_type;
@ -384,6 +484,35 @@ namespace lspd {
env->DeleteLocalRef(out5); env->DeleteLocalRef(out5);
env->DeleteLocalRef(out6); env->DeleteLocalRef(out6);
auto out7 = env->NewObjectArray(static_cast<jint>(2 * array_list.size()), object_class,
nullptr);
i = 0;
for (auto &array: array_list) {
auto out7i0 = env->NewIntArray(static_cast<jint>(array.size()));
auto out7i0_ptr = env->GetIntArrayElements(out7i0, nullptr);
auto out7i1 = env->NewObjectArray(static_cast<jint>(array.size()), object_class,
nullptr);
size_t j = 0;
for (auto &value: array) {
auto &[value_type, value_data] = value;
auto java_value = value_data.empty() ? nullptr : env->NewDirectByteBuffer(
value_data.data(), value_data.size());
out7i0_ptr[j] = value_type;
env->SetObjectArrayElement(out7i1, static_cast<jint>(j), java_value);
env->DeleteLocalRef(java_value);
++j;
}
env->ReleaseIntArrayElements(out7i0, out7i0_ptr, 0);
env->SetObjectArrayElement(out7, static_cast<jint>(2 * i), out7i0);
env->SetObjectArrayElement(out7, static_cast<jint>(2 * i + 1), out7i1);
env->DeleteLocalRef(out7i0);
env->DeleteLocalRef(out7i1);
++i;
}
env->SetObjectArrayElement(out, 7, out7);
env->DeleteLocalRef(out7);
return out; return out;
} }
@ -664,6 +793,8 @@ namespace lspd {
LSP_NATIVE_METHOD(DexParserBridge, openDex, LSP_NATIVE_METHOD(DexParserBridge, openDex,
"(Ljava/nio/buffer/ByteBuffer;[J)Ljava/lang/Object;"), "(Ljava/nio/buffer/ByteBuffer;[J)Ljava/lang/Object;"),
LSP_NATIVE_METHOD(DexParserBridge, closeDex, "(J)V;"), LSP_NATIVE_METHOD(DexParserBridge, closeDex, "(J)V;"),
LSP_NATIVE_METHOD(DexParserBridge, visitClass,
"(JLio/github/libxposed/utils/DexParser$ClassVisitor;Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)V"),
}; };

View File

@ -141,8 +141,8 @@ public class XposedContextWrapper extends ContextWrapper implements XposedInterf
@Nullable @Nullable
@Override @Override
final public DexParser parseDex(ByteBuffer dexData) throws IOException { final public DexParser parseDex(@NonNull ByteBuffer dexData, boolean includeAnnotations) throws IOException {
return getBaseContext().parseDex(dexData); return getBaseContext().parseDex(dexData, includeAnnotations);
} }
@Override @Override

View File

@ -158,5 +158,5 @@ public interface XposedInterface {
void log(@NonNull String message, @NonNull Throwable throwable); void log(@NonNull String message, @NonNull Throwable throwable);
@Nullable @Nullable
DexParser parseDex(ByteBuffer dexData) throws IOException; DexParser parseDex(@NonNull ByteBuffer dexData, boolean includeAnnotations) throws IOException;
} }

View File

@ -8,24 +8,32 @@ import java.io.Closeable;
public interface DexParser extends Closeable { public interface DexParser extends Closeable {
int NO_INDEX = 0xffffffff; int NO_INDEX = 0xffffffff;
interface Array {
@NonNull
Value[] getValues();
}
interface Annotation { interface Annotation {
int getVisibility(); int getVisibility();
@NonNull @NonNull
TypeId getType(); TypeId getType();
@Nullable @NonNull
AnnotationElement[] getElements(); Element[] getElements();
} }
interface AnnotationElement { interface Value {
@NonNull
StringId getName();
int getValueType();
@Nullable @Nullable
byte[] getValue(); byte[] getValue();
int getValueType();
}
interface Element extends Value {
@NonNull
StringId getName();
} }
interface TypeId { interface TypeId {
@ -93,6 +101,9 @@ public interface DexParser extends Closeable {
@NonNull @NonNull
Annotation[] getAnnotations(); Annotation[] getAnnotations();
@NonNull
Array[] getArrays();
interface EarlyStopVisitor { interface EarlyStopVisitor {
void stop(); void stop();
} }