blob: cd789952cca293e233f24f73df4b918437569bd4 [file] [log] [blame]
// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.dex;
import static com.android.tools.r8.utils.EncodedValueUtils.parseDouble;
import static com.android.tools.r8.utils.EncodedValueUtils.parseFloat;
import static com.android.tools.r8.utils.EncodedValueUtils.parseSigned;
import static com.android.tools.r8.utils.EncodedValueUtils.parseUnsigned;
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.code.Instruction;
import com.android.tools.r8.code.InstructionFactory;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.ClassKind;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationElement;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexCode.Try;
import com.android.tools.r8.graph.DexCode.TryHandler;
import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
import com.android.tools.r8.graph.DexDebugEvent;
import com.android.tools.r8.graph.DexDebugInfo;
import com.android.tools.r8.graph.DexEncodedAnnotation;
import com.android.tools.r8.graph.DexEncodedArray;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMember;
import com.android.tools.r8.graph.DexMemberAnnotation;
import com.android.tools.r8.graph.DexMemberAnnotation.DexFieldAnnotation;
import com.android.tools.r8.graph.DexMemberAnnotation.DexMethodAnnotation;
import com.android.tools.r8.graph.DexMemberAnnotation.DexParameterAnnotation;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueKind;
import com.android.tools.r8.graph.DexValue.DexValueNull;
import com.android.tools.r8.graph.EnclosingMethodAttribute;
import com.android.tools.r8.graph.FieldAccessFlags;
import com.android.tools.r8.graph.GenericSignature;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.OffsetToObjectMapping;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Pair;
import com.google.common.io.ByteStreams;
import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2LongMap;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ShortBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class DexParser<T extends DexClass> {
private final int NO_INDEX = -1;
private final Origin origin;
private DexReader dexReader;
private final DexSection[] dexSections;
private int[] stringIDs;
private final ClassKind<T> classKind;
private final InternalOptions options;
private Object2LongMap<String> checksums;
public static DexSection[] parseMapFrom(Path file) throws IOException {
return parseMapFrom(Files.newInputStream(file), new PathOrigin(file));
}
public static DexSection[] parseMapFrom(InputStream stream, Origin origin) throws IOException {
return parseMapFrom(new DexReader(origin, ByteStreams.toByteArray(stream)));
}
private static DexSection[] parseMapFrom(DexReader dexReader) {
DexParser<DexProgramClass> dexParser =
new DexParser<>(dexReader, ClassKind.PROGRAM, new InternalOptions());
return dexParser.dexSections;
}
public void close() {
// This close behavior is needed to reduce peak memory usage of D8/R8.
indexedItems = null;
codes = null;
offsetMap = null;
dexReader = null;
stringIDs = null;
}
// Mapping from indexes to indexable dex items.
private OffsetToObjectMapping indexedItems = new OffsetToObjectMapping();
// Mapping from offset to code item;
private Int2ReferenceMap<DexCode> codes = new Int2ReferenceOpenHashMap<>();
// Mapping from offset to dex item;
private Int2ReferenceMap<Object> offsetMap = new Int2ReferenceOpenHashMap<>();
// Factory to canonicalize certain dexitems.
private final DexItemFactory dexItemFactory;
public DexParser(DexReader dexReader, ClassKind<T> classKind, InternalOptions options) {
assert dexReader.getOrigin() != null;
this.origin = dexReader.getOrigin();
this.dexReader = dexReader;
this.dexItemFactory = options.itemFactory;
dexReader.setByteOrder();
dexSections = parseMap();
parseStringIDs();
this.classKind = classKind;
this.options = options;
}
private void ensureCodesInited(int offset) {
if (offset == 0) {
return;
}
if (codes == null) {
codes = new Int2ReferenceOpenHashMap<>();
}
if (classKind == ClassKind.LIBRARY) {
// Ignore contents of library files.
return;
}
DexSection dexSection = lookupSection(Constants.TYPE_CODE_ITEM);
if (dexSection.length == 0) {
return;
}
if (!codes.containsKey(offset)) {
int currentPos = dexReader.position();
dexReader.position(offset);
dexReader.align(4);
DexCode code = parseCodeItem();
codes.put(offset, code); // Update the file local offset to code mapping.
dexReader.position(currentPos);
}
}
private DexTypeList parseTypeList() {
DexType[] result = new DexType[dexReader.getUint()];
for (int j = 0; j < result.length; j++) {
result[j] = indexedItems.getType(dexReader.getUshort());
}
return new DexTypeList(result);
}
private DexTypeList typeListAt(int offset) {
if (offset == 0) {
return DexTypeList.empty();
}
return (DexTypeList) cacheAt(offset, this::parseTypeList);
}
private DexValue parseEncodedValue() {
int header = dexReader.get() & 0xff;
int valueArg = header >> 5;
int valueType = header & 0x1f;
switch (DexValueKind.fromId(valueType)) {
case BYTE:
{
assert valueArg == 0;
byte value = (byte) parseSigned(dexReader, 1);
return DexValue.DexValueByte.create(value);
}
case SHORT:
{
int size = valueArg + 1;
short value = (short) parseSigned(dexReader, size);
return DexValue.DexValueShort.create(value);
}
case CHAR:
{
int size = valueArg + 1;
char value = (char) parseUnsigned(dexReader, size);
return DexValue.DexValueChar.create(value);
}
case INT:
{
int size = valueArg + 1;
int value = (int) parseSigned(dexReader, size);
return DexValue.DexValueInt.create(value);
}
case LONG:
{
int size = valueArg + 1;
long value = parseSigned(dexReader, size);
return DexValue.DexValueLong.create(value);
}
case FLOAT:
{
int size = valueArg + 1;
return DexValue.DexValueFloat.create(parseFloat(dexReader, size));
}
case DOUBLE:
{
int size = valueArg + 1;
return DexValue.DexValueDouble.create(parseDouble(dexReader, size));
}
case STRING:
{
int size = valueArg + 1;
int index = (int) parseUnsigned(dexReader, size);
DexString value = indexedItems.getString(index);
return new DexValue.DexValueString(value);
}
case TYPE:
{
int size = valueArg + 1;
DexType value = indexedItems.getType((int) parseUnsigned(dexReader, size));
return new DexValue.DexValueType(value);
}
case FIELD:
{
int size = valueArg + 1;
DexField value = indexedItems.getField((int) parseUnsigned(dexReader, size));
checkName(value.name);
return new DexValue.DexValueField(value);
}
case METHOD:
{
int size = valueArg + 1;
DexMethod value = indexedItems.getMethod((int) parseUnsigned(dexReader, size));
checkName(value.name);
return new DexValue.DexValueMethod(value);
}
case ENUM:
{
int size = valueArg + 1;
DexField value = indexedItems.getField((int) parseUnsigned(dexReader, size));
return new DexValue.DexValueEnum(value);
}
case ARRAY:
{
assert valueArg == 0;
return new DexValue.DexValueArray(parseEncodedArrayValues());
}
case ANNOTATION:
{
assert valueArg == 0;
return new DexValue.DexValueAnnotation(parseEncodedAnnotation());
}
case NULL:
{
assert valueArg == 0;
return DexValueNull.NULL;
}
case BOOLEAN:
{
// 0 is false, and 1 is true.
return DexValue.DexValueBoolean.create(valueArg != 0);
}
case METHOD_TYPE:
{
int size = valueArg + 1;
DexProto value = indexedItems.getProto((int) parseUnsigned(dexReader, size));
return new DexValue.DexValueMethodType(value);
}
case METHOD_HANDLE:
{
int size = valueArg + 1;
DexMethodHandle value =
indexedItems.getMethodHandle((int) parseUnsigned(dexReader, size));
return new DexValue.DexValueMethodHandle(value);
}
default:
throw new IndexOutOfBoundsException();
}
}
private void checkName(DexString name) {
if (!options.itemFactory.getSkipNameValidationForTesting()
&& !name.isValidSimpleName(options.minApiLevel)) {
throw new CompilationError("Space characters in SimpleName '"
+ name.toASCIIString()
+ "' are not allowed prior to DEX version 040");
}
}
private DexEncodedAnnotation parseEncodedAnnotation() {
int typeIdx = dexReader.getUleb128();
int size = dexReader.getUleb128();
DexAnnotationElement[] elements = new DexAnnotationElement[size];
for (int i = 0; i < size; i++) {
int nameIdx = dexReader.getUleb128();
DexValue value = parseEncodedValue();
elements[i] = new DexAnnotationElement(indexedItems.getString(nameIdx), value);
}
return new DexEncodedAnnotation(indexedItems.getType(typeIdx), elements);
}
private DexValue[] parseEncodedArrayValues() {
int size = dexReader.getUleb128();
DexValue[] values = new DexValue[size];
for (int i = 0; i < size; i++) {
values[i] = parseEncodedValue();
}
return values;
}
private DexEncodedArray parseEncodedArray() {
return new DexEncodedArray(parseEncodedArrayValues());
}
private DexEncodedArray encodedArrayAt(int offset) {
return (DexEncodedArray) cacheAt(offset, this::parseEncodedArray);
}
private DexFieldAnnotation[] parseFieldAnnotations(int size) {
if (size == 0) {
return null;
}
int[] fieldIndices = new int[size];
int[] annotationOffsets = new int[size];
for (int i = 0; i < size; i++) {
fieldIndices[i] = dexReader.getUint();
annotationOffsets[i] = dexReader.getUint();
}
int saved = dexReader.position();
DexFieldAnnotation[] result = new DexFieldAnnotation[size];
for (int i = 0; i < size; i++) {
DexField field = indexedItems.getField(fieldIndices[i]);
DexAnnotationSet annotation = annotationSetAt(annotationOffsets[i]);
result[i] = new DexFieldAnnotation(field, annotation);
}
dexReader.position(saved);
return result;
}
private DexMethodAnnotation[] parseMethodAnnotations(int size) {
if (size == 0) {
return null;
}
int[] methodIndices = new int[size];
int[] annotationOffsets = new int[size];
for (int i = 0; i < size; i++) {
methodIndices[i] = dexReader.getUint();
annotationOffsets[i] = dexReader.getUint();
}
int saved = dexReader.position();
DexMethodAnnotation[] result = new DexMethodAnnotation[size];
for (int i = 0; i < size; i++) {
DexMethod method = indexedItems.getMethod(methodIndices[i]);
DexAnnotationSet annotation = annotationSetAt(annotationOffsets[i]);
result[i] = new DexMethodAnnotation(method, annotation);
}
dexReader.position(saved);
return result;
}
private ParameterAnnotationsList annotationSetRefListAt(int offset) {
return (ParameterAnnotationsList) cacheAt(offset, this::parseAnnotationSetRefList);
}
private ParameterAnnotationsList parseAnnotationSetRefList() {
int size = dexReader.getUint();
int[] annotationOffsets = new int[size];
for (int i = 0; i < size; i++) {
annotationOffsets[i] = dexReader.getUint();
}
DexAnnotationSet[] values = new DexAnnotationSet[size];
for (int i = 0; i < size; i++) {
values[i] = annotationSetAt(annotationOffsets[i]);
}
return new ParameterAnnotationsList(values);
}
private DexParameterAnnotation[] parseParameterAnnotations(int size) {
if (size == 0) {
return null;
}
int[] methodIndices = new int[size];
int[] annotationOffsets = new int[size];
for (int i = 0; i < size; i++) {
methodIndices[i] = dexReader.getUint();
annotationOffsets[i] = dexReader.getUint();
}
int saved = dexReader.position();
DexParameterAnnotation[] result = new DexParameterAnnotation[size];
for (int i = 0; i < size; i++) {
DexMethod method = indexedItems.getMethod(methodIndices[i]);
result[i] = new DexParameterAnnotation(
method,
annotationSetRefListAt(annotationOffsets[i])
.withParameterCount(method.proto.parameters.size()));
}
dexReader.position(saved);
return result;
}
private <S> Object cacheAt(int offset, Supplier<S> function, Supplier<S> defaultValue) {
if (offset == 0) {
return defaultValue.get();
}
return cacheAt(offset, function);
}
private <S> Object cacheAt(int offset, Supplier<S> function) {
if (offset == 0) {
return null; // return null for offset zero.
}
Object result = offsetMap.get(offset);
if (result != null) {
return result; // return the cached result.
}
// Cache is empty so parse the structure.
dexReader.position(offset);
result = function.get();
// Update the map.
offsetMap.put(offset, result);
assert offsetMap.get(offset) == result;
return result;
}
private DexAnnotation parseAnnotation() {
if (Log.ENABLED) {
Log.verbose(getClass(), "Reading Annotation @ 0x%08x.", dexReader.position());
}
int visibility = dexReader.get();
return new DexAnnotation(visibility, parseEncodedAnnotation());
}
private DexAnnotation annotationAt(int offset) {
return (DexAnnotation) cacheAt(offset, this::parseAnnotation);
}
private DexAnnotationSet parseAnnotationSet() {
if (Log.ENABLED) {
Log.verbose(getClass(), "Reading AnnotationSet @ 0x%08x.", dexReader.position());
}
int size = dexReader.getUint();
int[] annotationOffsets = new int[size];
for (int i = 0; i < size; i++) {
annotationOffsets[i] = dexReader.getUint();
}
DexAnnotation[] result = new DexAnnotation[size];
int actualSize = 0;
for (int i = 0; i < size; i++) {
DexAnnotation dexAnnotation = annotationAt(annotationOffsets[i]);
if (retainAnnotation(dexAnnotation)) {
result[actualSize++] = dexAnnotation;
}
}
if (actualSize < size) {
DexAnnotation[] temp = new DexAnnotation[actualSize];
System.arraycopy(result, 0, temp, 0, actualSize);
result = temp;
}
DexType dupType = DexAnnotationSet.findDuplicateEntryType(result);
if (dupType != null) {
throw new CompilationError(
"Multiple annotations of type `" + dupType.toSourceString() + "`");
}
return new DexAnnotationSet(result);
}
private boolean retainAnnotation(DexAnnotation annotation) {
return annotation.visibility != DexAnnotation.VISIBILITY_BUILD
|| DexAnnotation.retainCompileTimeAnnotation(annotation.annotation.type, options);
}
private DexAnnotationSet annotationSetAt(int offset) {
return (DexAnnotationSet) cacheAt(offset, this::parseAnnotationSet, DexAnnotationSet::empty);
}
private AnnotationsDirectory annotationsDirectoryAt(int offset) {
return (AnnotationsDirectory) cacheAt(offset, this::parseAnnotationsDirectory,
AnnotationsDirectory::empty);
}
private AnnotationsDirectory parseAnnotationsDirectory() {
int classAnnotationsOff = dexReader.getUint();
int fieldsSize = dexReader.getUint();
int methodsSize = dexReader.getUint();
int parametersSize = dexReader.getUint();
final DexFieldAnnotation[] fields = parseFieldAnnotations(fieldsSize);
final DexMethodAnnotation[] methods = parseMethodAnnotations(methodsSize);
final DexParameterAnnotation[] parameters = parseParameterAnnotations(parametersSize);
return new AnnotationsDirectory(
annotationSetAt(classAnnotationsOff),
fields,
methods,
parameters);
}
private DexDebugInfo debugInfoAt(int offset) {
return (DexDebugInfo) cacheAt(offset, this::parseDebugInfo);
}
private DexDebugInfo parseDebugInfo() {
int start = dexReader.getUleb128();
int parametersSize = dexReader.getUleb128();
DexString[] parameters = new DexString[parametersSize];
for (int i = 0; i < parametersSize; i++) {
int index = dexReader.getUleb128p1();
if (index != NO_INDEX) {
parameters[i] = indexedItems.getString(index);
}
}
List<DexDebugEvent> events = new ArrayList<>();
for (int head = dexReader.getUbyte(); head != Constants.DBG_END_SEQUENCE; head = dexReader.getUbyte()) {
switch (head) {
case Constants.DBG_ADVANCE_PC:
events.add(dexItemFactory.createAdvancePC(dexReader.getUleb128()));
break;
case Constants.DBG_ADVANCE_LINE:
events.add(dexItemFactory.createAdvanceLine(dexReader.getSleb128()));
break;
case Constants.DBG_START_LOCAL: {
int registerNum = dexReader.getUleb128();
int nameIdx = dexReader.getUleb128p1();
int typeIdx = dexReader.getUleb128p1();
events.add(new DexDebugEvent.StartLocal(
registerNum,
nameIdx == NO_INDEX ? null : indexedItems.getString(nameIdx),
typeIdx == NO_INDEX ? null : indexedItems.getType(typeIdx),
null));
break;
}
case Constants.DBG_START_LOCAL_EXTENDED: {
int registerNum = dexReader.getUleb128();
int nameIdx = dexReader.getUleb128p1();
int typeIdx = dexReader.getUleb128p1();
int sigIdx = dexReader.getUleb128p1();
events.add(new DexDebugEvent.StartLocal(
registerNum,
nameIdx == NO_INDEX ? null : indexedItems.getString(nameIdx),
typeIdx == NO_INDEX ? null : indexedItems.getType(typeIdx),
sigIdx == NO_INDEX ? null : indexedItems.getString(sigIdx)));
break;
}
case Constants.DBG_END_LOCAL: {
events.add(dexItemFactory.createEndLocal(dexReader.getUleb128()));
break;
}
case Constants.DBG_RESTART_LOCAL: {
events.add(dexItemFactory.createRestartLocal(dexReader.getUleb128()));
break;
}
case Constants.DBG_SET_PROLOGUE_END: {
events.add(dexItemFactory.createSetPrologueEnd());
break;
}
case Constants.DBG_SET_EPILOGUE_BEGIN: {
events.add(dexItemFactory.createSetEpilogueBegin());
break;
}
case Constants.DBG_SET_FILE: {
int nameIdx = dexReader.getUleb128p1();
DexString sourceFile = nameIdx == NO_INDEX ? null : indexedItems.getString(nameIdx);
events.add(dexItemFactory.createSetFile(sourceFile));
break;
}
default: {
assert head >= 0x0a && head <= 0xff;
events.add(dexItemFactory.createDefault(head));
}
}
}
return new DexDebugInfo(start, parameters, events.toArray(DexDebugEvent.EMPTY_ARRAY));
}
private static class MemberAnnotationIterator<R extends DexMember<?, R>, T extends DexItem> {
private int index = 0;
private final DexMemberAnnotation<R, T>[] annotations;
private final Supplier<T> emptyValue;
private MemberAnnotationIterator(
DexMemberAnnotation<R, T>[] annotations, Supplier<T> emptyValue) {
this.annotations = annotations;
this.emptyValue = emptyValue;
}
// Get the annotation set for an item. This method assumes that it is always called with
// an item that is higher in the sorting order than the last item.
T getNextFor(R item) {
// TODO(ager): We could use the indices from the file to make this search faster using
// compareTo instead of slowCompareTo. That would require us to assign indices during
// reading. Those indices should be cleared after reading to make sure that we resort
// everything correctly at the end.
while (index < annotations.length && annotations[index].item.compareTo(item) < 0) {
index++;
}
if (index >= annotations.length || !annotations[index].item.equals(item)) {
return emptyValue.get();
}
return annotations[index].annotations;
}
}
private DexEncodedField[] readFields(int size, DexFieldAnnotation[] annotations,
DexValue[] staticValues) {
DexEncodedField[] fields = new DexEncodedField[size];
int fieldIndex = 0;
MemberAnnotationIterator<DexField, DexAnnotationSet> annotationIterator =
new MemberAnnotationIterator<>(annotations, DexAnnotationSet::empty);
for (int i = 0; i < size; i++) {
fieldIndex += dexReader.getUleb128();
DexField field = indexedItems.getField(fieldIndex);
FieldAccessFlags accessFlags = FieldAccessFlags.fromDexAccessFlags(dexReader.getUleb128());
DexValue staticValue = null;
if (accessFlags.isStatic()) {
if (staticValues != null && i < staticValues.length) {
staticValue = staticValues[i];
}
}
DexAnnotationSet fieldAnnotations = annotationIterator.getNextFor(field);
FieldTypeSignature fieldTypeSignature = FieldTypeSignature.noSignature();
if (!options.passthroughDexCode) {
String fieldSignature = DexAnnotation.getSignature(fieldAnnotations, dexItemFactory);
if (fieldSignature != null) {
fieldAnnotations = fieldAnnotations.getWithout(dexItemFactory.annotationSignature);
fieldTypeSignature =
GenericSignature.parseFieldTypeSignature(
field.name.toString(), fieldSignature, origin, dexItemFactory, options.reporter);
}
}
fields[i] =
new DexEncodedField(
field, accessFlags, fieldTypeSignature, fieldAnnotations, staticValue);
}
return fields;
}
private DexEncodedMethod[] readMethods(
int size,
DexMethodAnnotation[] annotations,
DexParameterAnnotation[] parameters,
boolean skipCodes) {
DexEncodedMethod[] methods = new DexEncodedMethod[size];
int methodIndex = 0;
MemberAnnotationIterator<DexMethod, DexAnnotationSet> annotationIterator =
new MemberAnnotationIterator<>(annotations, DexAnnotationSet::empty);
MemberAnnotationIterator<DexMethod, ParameterAnnotationsList> parameterAnnotationsIterator =
new MemberAnnotationIterator<>(parameters, ParameterAnnotationsList::empty);
for (int i = 0; i < size; i++) {
methodIndex += dexReader.getUleb128();
MethodAccessFlags accessFlags = MethodAccessFlags.fromDexAccessFlags(dexReader.getUleb128());
int codeOff = dexReader.getUleb128();
DexCode code = null;
if (!skipCodes) {
ensureCodesInited(codeOff);
assert codeOff == 0 || codes.get(codeOff) != null;
code = codes.get(codeOff);
}
DexMethod method = indexedItems.getMethod(methodIndex);
accessFlags.setConstructor(method, dexItemFactory);
DexAnnotationSet methodAnnotations = annotationIterator.getNextFor(method);
MethodTypeSignature methodTypeSignature = MethodTypeSignature.noSignature();
if (!options.passthroughDexCode) {
String methodSignature = DexAnnotation.getSignature(methodAnnotations, dexItemFactory);
if (methodSignature != null) {
methodAnnotations = methodAnnotations.getWithout(dexItemFactory.annotationSignature);
methodTypeSignature =
GenericSignature.parseMethodSignature(
method.name.toString(),
methodSignature,
origin,
dexItemFactory,
options.reporter);
}
}
methods[i] =
new DexEncodedMethod(
method,
accessFlags,
methodTypeSignature,
methodAnnotations,
parameterAnnotationsIterator.getNextFor(method),
code);
}
return methods;
}
void addClassDefsTo(Consumer<T> classCollection) {
final DexSection dexSection = lookupSection(Constants.TYPE_CLASS_DEF_ITEM);
final int length = dexSection.length;
indexedItems.initializeClasses(length);
if (length == 0) {
return;
}
dexReader.position(dexSection.offset);
int[] classIndices = new int[length];
int[] accessFlags = new int[length];
int[] superclassIndices = new int[length];
int[] interfacesOffsets = new int[length];
int[] sourceFileIndices = new int[length];
int[] annotationsOffsets = new int[length];
int[] classDataOffsets = new int[length];
int[] staticValuesOffsets = new int[length];
for (int i = 0; i < length; i++) {
if (Log.ENABLED) {
Log.verbose(getClass(), "Reading ClassDef @ 0x%08x.", dexReader.position());
}
classIndices[i] = dexReader.getUint();
accessFlags[i] = dexReader.getUint();
superclassIndices[i] = dexReader.getInt();
interfacesOffsets[i] = dexReader.getUint();
sourceFileIndices[i] = dexReader.getInt();
annotationsOffsets[i] = dexReader.getUint();
classDataOffsets[i] = dexReader.getUint();
staticValuesOffsets[i] = dexReader.getUint();
}
for (int i = 0; i < length; i++) {
int superclassIdx = superclassIndices[i];
DexType superclass = superclassIdx == NO_INDEX ? null : indexedItems.getType(superclassIdx);
int srcIdx = sourceFileIndices[i];
DexString source = srcIdx == NO_INDEX ? null : indexedItems.getString(srcIdx);
DexType type = indexedItems.getType(classIndices[i]);
ClassAccessFlags flags = ClassAccessFlags.fromDexAccessFlags(accessFlags[i]);
// Check if constraints from
// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1 are met.
if (!flags.areValid(Constants.CORRESPONDING_CLASS_FILE_VERSION, false)) {
throw new CompilationError("Class " + type.toSourceString()
+ " has illegal access flags. Found: " + flags, origin);
}
DexEncodedField[] staticFields = DexEncodedField.EMPTY_ARRAY;
DexEncodedField[] instanceFields = DexEncodedField.EMPTY_ARRAY;
DexEncodedMethod[] directMethods = DexEncodedMethod.EMPTY_ARRAY;
DexEncodedMethod[] virtualMethods = DexEncodedMethod.EMPTY_ARRAY;
AnnotationsDirectory annotationsDirectory = annotationsDirectoryAt(annotationsOffsets[i]);
Long checksum = null;
if (checksums != null && !checksums.isEmpty()) {
String desc = type.toDescriptorString();
checksum = checksums.getOrDefault(desc, null);
if (!options.dexClassChecksumFilter.test(desc, checksum)) {
continue;
}
}
if (classDataOffsets[i] != 0) {
DexEncodedArray staticValues = encodedArrayAt(staticValuesOffsets[i]);
dexReader.position(classDataOffsets[i]);
int staticFieldsSize = dexReader.getUleb128();
int instanceFieldsSize = dexReader.getUleb128();
int directMethodsSize = dexReader.getUleb128();
int virtualMethodsSize = dexReader.getUleb128();
staticFields = readFields(staticFieldsSize, annotationsDirectory.fields,
staticValues != null ? staticValues.values : null);
instanceFields = readFields(instanceFieldsSize, annotationsDirectory.fields, null);
directMethods =
readMethods(
directMethodsSize,
annotationsDirectory.methods,
annotationsDirectory.parameters,
classKind != ClassKind.PROGRAM);
virtualMethods =
readMethods(
virtualMethodsSize,
annotationsDirectory.methods,
annotationsDirectory.parameters,
classKind != ClassKind.PROGRAM);
}
AttributesAndAnnotations attrs =
new AttributesAndAnnotations(type, origin, annotationsDirectory.clazz, options);
Long finalChecksum = checksum;
ChecksumSupplier checksumSupplier =
finalChecksum == null ? DexProgramClass::invalidChecksumRequest : c -> finalChecksum;
T clazz =
classKind.create(
type,
Kind.DEX,
origin,
flags,
superclass,
typeListAt(interfacesOffsets[i]),
source,
null,
Collections.emptyList(),
attrs.getEnclosingMethodAttribute(),
attrs.getInnerClasses(),
attrs.classSignature,
attrs.getAnnotations(),
staticFields,
instanceFields,
directMethods,
virtualMethods,
dexItemFactory.getSkipNameValidationForTesting(),
checksumSupplier);
classCollection.accept(clazz); // Update the application object.
}
}
private void parseStringIDs() {
DexSection dexSection = lookupSection(Constants.TYPE_STRING_ID_ITEM);
stringIDs = new int[dexSection.length];
if (dexSection.length == 0) {
return;
}
dexReader.position(dexSection.offset);
for (int i = 0; i < dexSection.length; i++) {
stringIDs[i] = dexReader.getUint();
}
}
private DexSection lookupSection(int type) {
for (DexSection s : dexSections) {
if (s.type == type) {
return s;
}
}
// If the section doesn't exist, return an empty section of this type.
return new DexSection(type, 0, 0, 0);
}
private DexSection[] parseMap() {
// Read the dexSections information from the MAP.
int mapOffset = dexReader.getUint(Constants.MAP_OFF_OFFSET);
dexReader.position(mapOffset);
int mapSize = dexReader.getUint();
final DexSection[] result = new DexSection[mapSize];
for (int i = 0; i < mapSize; i++) {
int type = dexReader.getUshort();
int unused = dexReader.getUshort();
int size = dexReader.getUint();
int offset = dexReader.getUint();
if (offset + size > dexReader.end()) {
throw new CompilationError(
"The dex file had an offset + size that pointed past the end of the dex file."
+ "\nSection type: "
+ DexSection.typeName(type)
+ "\nSection offset: "
+ offset
+ "\nSection size: "
+ size
+ "\nFile size: "
+ dexReader.end(),
origin);
}
result[i] = new DexSection(type, unused, size, offset);
}
if (Log.ENABLED) {
for (int i = 0; i < result.length; i++) {
DexSection dexSection = result[i];
int nextOffset = i < result.length - 1 ? result[i + 1].offset : dexSection.offset;
Log.debug(this.getClass(), "Read section 0x%04x @ 0x%08x #items %08d size 0x%08x.",
dexSection.type, dexSection.offset, dexSection.length, nextOffset - dexSection.offset);
}
}
for (int i = 0; i < mapSize - 1; i++) {
result[i].setEnd(result[i + 1].offset);
}
result[mapSize - 1].setEnd(dexReader.end());
return result;
}
private DexCode parseCodeItem() {
int registerSize = dexReader.getUshort();
int insSize = dexReader.getUshort();
int outsSize = dexReader.getUshort();
int triesSize = dexReader.getUshort();
int debugInfoOff = dexReader.getUint();
int insnsSize = dexReader.getUint();
short[] code = new short[insnsSize];
Try[] tries = new Try[triesSize];
TryHandler[] handlers = new TryHandler[0];
if (insnsSize != 0) {
for (int i = 0; i < insnsSize; i++) {
code[i] = dexReader.getShort();
}
if (insnsSize % 2 != 0) {
dexReader.getUshort(); // Skip padding ushort
}
if (triesSize > 0) {
Int2IntArrayMap handlerMap = new Int2IntArrayMap();
// tries: try_item[tries_size].
for (int i = 0; i < triesSize; i++) {
int startAddr = dexReader.getUint();
int insnCount = dexReader.getUshort();
int handlerOff = dexReader.getUshort();
tries[i] = new Try(startAddr, insnCount, handlerOff);
}
// handlers: encoded_catch_handler_list
int encodedCatchHandlerListPosition = dexReader.position();
// - size: uleb128
int size = dexReader.getUleb128();
handlers = new TryHandler[size];
// - list: encoded_catch_handler[handlers_size]
for (int i = 0; i < size; i++) {
// encoded_catch_handler
int encodedCatchHandlerOffset = dexReader.position() - encodedCatchHandlerListPosition;
handlerMap.put(encodedCatchHandlerOffset, i);
// - size: sleb128
int hsize = dexReader.getSleb128();
int realHsize = Math.abs(hsize);
// - handlers encoded_type_addr_pair[abs(size)]
TryHandler.TypeAddrPair[] pairs = new TryHandler.TypeAddrPair[realHsize];
for (int j = 0; j < realHsize; j++) {
int typeIdx = dexReader.getUleb128();
int addr = dexReader.getUleb128();
pairs[j] = new TypeAddrPair(indexedItems.getType(typeIdx), addr);
}
int catchAllAddr = -1;
if (hsize <= 0) {
catchAllAddr = dexReader.getUleb128();
}
handlers[i] = new TryHandler(pairs, catchAllAddr);
}
// Convert the handler offsets inside the Try objects to indexes.
for (Try t : tries) {
t.setHandlerIndex(handlerMap);
}
}
}
// Store and restore offset information around reading debug info.
int saved = dexReader.position();
DexDebugInfo debugInfo = debugInfoAt(debugInfoOff);
dexReader.position(saved);
InstructionFactory factory = new InstructionFactory();
Instruction[] instructions =
factory.readSequenceFrom(ShortBuffer.wrap(code), 0, code.length, indexedItems);
return new DexCode(registerSize, insSize, outsSize, instructions, tries, handlers, debugInfo);
}
void populateIndexTables() {
// Populate structures that are already sorted upon read.
populateStrings(); // Depends on nothing.
populateChecksums(); // Depends on Strings.
populateTypes(); // Depends on Strings.
populateFields(); // Depends on Types, and Strings.
populateProtos(); // Depends on Types and Strings.
populateMethods(); // Depends on Protos, Types, and Strings.
populateMethodHandles(); // Depends on Methods and Fields
populateCallSites(); // Depends on MethodHandles
}
private void populateStrings() {
indexedItems.initializeStrings(stringIDs.length);
for (int i = 0; i < stringIDs.length; i++) {
indexedItems.setString(i, stringAt(i));
}
}
private void populateMethodHandles() {
DexSection dexSection = lookupSection(Constants.TYPE_METHOD_HANDLE_ITEM);
indexedItems.initializeMethodHandles(dexSection.length);
for (int i = 0; i < dexSection.length; i++) {
indexedItems.setMethodHandle(i, methodHandleAt(i));
}
}
private void populateCallSites() {
DexSection dexSection = lookupSection(Constants.TYPE_CALL_SITE_ID_ITEM);
indexedItems.initializeCallSites(dexSection.length);
for (int i = 0; i < dexSection.length; i++) {
indexedItems.setCallSites(i, callSiteAt(i));
}
}
private void populateTypes() {
DexSection dexSection = lookupSection(Constants.TYPE_TYPE_ID_ITEM);
assert verifyOrderOfTypeIds(dexSection);
indexedItems.initializeTypes(dexSection.length);
for (int i = 0; i < dexSection.length; i++) {
indexedItems.setType(i, typeAt(i));
}
}
private void populateChecksums() {
ClassesChecksum parsedChecksums = new ClassesChecksum();
for (int i = stringIDs.length - 1; i >= 0; i--) {
DexString value = indexedItems.getString(i);
if (ClassesChecksum.definitelyPrecedesChecksumMarker(value)) {
break;
}
parsedChecksums.tryParseAndAppend(value);
}
this.checksums = parsedChecksums.getChecksums();
}
/**
* From https://source.android.com/devices/tech/dalvik/dex-format#file-layout:
*
* <p>This list must be sorted by string_id index, and it must not contain any duplicate entries.
*/
private boolean verifyOrderOfTypeIds(DexSection dexSection) {
if (dexSection.length >= 2) {
int initialOffset = dexSection.offset;
dexReader.position(initialOffset);
int prevStringIndex = dexReader.getUint();
for (int index = 1; index < dexSection.length; index++) {
int offset = initialOffset + Constants.TYPE_TYPE_ID_ITEM_SIZE * index;
dexReader.position(offset);
int stringIndex = dexReader.getUint();
boolean isValidOrder = stringIndex > prevStringIndex;
assert isValidOrder
: String.format(
"Out-of-order type ids (type #%s: `%s`, type #%s: `%s`)",
index - 1,
indexedItems.getString(prevStringIndex),
index,
indexedItems.getString(stringIndex));
prevStringIndex = stringIndex;
}
}
return true;
}
private void populateFields() {
DexSection dexSection = lookupSection(Constants.TYPE_FIELD_ID_ITEM);
assert verifyOrderOfFieldIds(dexSection);
indexedItems.initializeFields(dexSection.length);
for (int i = 0; i < dexSection.length; i++) {
indexedItems.setField(i, fieldAt(i));
}
}
/**
* From https://source.android.com/devices/tech/dalvik/dex-format#file-layout:
*
* <p>This list must be sorted, where the defining type (by type_id index) is the major order,
* field name (by string_id index) is the intermediate order, and type (by type_id index) is the
* minor order. The list must not contain any duplicate entries.
*/
private boolean verifyOrderOfFieldIds(DexSection dexSection) {
if (dexSection.length >= 2) {
int initialOffset = dexSection.offset;
dexReader.position(initialOffset);
int prevHolderIndex = dexReader.getUshort();
int prevTypeIndex = dexReader.getUshort();
int prevNameIndex = dexReader.getUint();
for (int index = 1; index < dexSection.length; index++) {
int offset = initialOffset + Constants.TYPE_FIELD_ID_ITEM_SIZE * index;
dexReader.position(offset);
int holderIndex = dexReader.getUshort();
int typeIndex = dexReader.getUshort();
int nameIndex = dexReader.getUint();
boolean isValidOrder;
if (holderIndex == prevHolderIndex) {
if (nameIndex == prevNameIndex) {
isValidOrder = typeIndex > prevTypeIndex;
} else {
isValidOrder = nameIndex > prevNameIndex;
}
} else {
isValidOrder = holderIndex > prevHolderIndex;
}
assert isValidOrder
: String.format(
"Out-of-order field ids (field #%s: `%s`, field #%s: `%s`)",
index - 1,
dexItemFactory
.createField(
indexedItems.getType(prevHolderIndex),
indexedItems.getType(prevTypeIndex),
indexedItems.getString(prevNameIndex))
.toSourceString(),
index,
dexItemFactory
.createField(
indexedItems.getType(holderIndex),
indexedItems.getType(typeIndex),
indexedItems.getString(nameIndex))
.toSourceString());
prevHolderIndex = holderIndex;
prevTypeIndex = typeIndex;
prevNameIndex = nameIndex;
}
}
return true;
}
private void populateProtos() {
DexSection dexSection = lookupSection(Constants.TYPE_PROTO_ID_ITEM);
indexedItems.initializeProtos(dexSection.length);
for (int i = 0; i < dexSection.length; i++) {
indexedItems.setProto(i, protoAt(i));
}
}
private void populateMethods() {
DexSection dexSection = lookupSection(Constants.TYPE_METHOD_ID_ITEM);
assert verifyOrderOfMethodIds(dexSection);
indexedItems.initializeMethods(dexSection.length);
for (int i = 0; i < dexSection.length; i++) {
indexedItems.setMethod(i, methodAt(i));
}
}
/**
* From https://source.android.com/devices/tech/dalvik/dex-format#file-layout:
*
* <p>This list must be sorted, where the defining type (by type_id index) is the major order,
* method name (by string_id index) is the intermediate order, and method prototype
* (by proto_id index) is the minor order. The list must not contain any duplicate entries.
*
*/
private boolean verifyOrderOfMethodIds(DexSection dexSection) {
if (dexSection.length >= 2) {
int initialOffset = dexSection.offset;
dexReader.position(initialOffset);
int prevHolderIndex = dexReader.getUshort();
int prevProtoIndex = dexReader.getUshort();
int prevNameIndex = dexReader.getUint();
for (int index = 1; index < dexSection.length; index++) {
int offset = initialOffset + Constants.TYPE_METHOD_ID_ITEM_SIZE * index;
dexReader.position(offset);
int holderIndex = dexReader.getUshort();
int protoIndex = dexReader.getUshort();
int nameIndex = dexReader.getUint();
boolean isValidOrder;
if (holderIndex == prevHolderIndex) {
if (nameIndex == prevNameIndex) {
isValidOrder = protoIndex > prevProtoIndex;
} else {
isValidOrder = nameIndex > prevNameIndex;
}
} else {
isValidOrder = holderIndex > prevHolderIndex;
}
assert isValidOrder
: String.format(
"Out-of-order method ids (method #%s: `%s`, method #%s: `%s`)",
index - 1,
dexItemFactory
.createMethod(
indexedItems.getType(prevHolderIndex),
indexedItems.getProto(prevProtoIndex),
indexedItems.getString(prevNameIndex))
.toSourceString(),
index,
dexItemFactory
.createMethod(
indexedItems.getType(holderIndex),
indexedItems.getProto(protoIndex),
indexedItems.getString(nameIndex))
.toSourceString());
prevHolderIndex = holderIndex;
prevProtoIndex = protoIndex;
prevNameIndex = nameIndex;
}
}
return true;
}
private DexString stringAt(int index) {
final int offset = stringIDs[index];
dexReader.position(offset);
int size = dexReader.getUleb128();
ByteArrayOutputStream os = new ByteArrayOutputStream();
byte read;
do {
read = dexReader.get();
os.write(read);
} while (read != 0);
return dexItemFactory.createString(size, os.toByteArray());
}
private DexType typeAt(int index) {
DexSection dexSection = lookupSection(Constants.TYPE_TYPE_ID_ITEM);
if (index >= dexSection.length) {
return null;
}
int offset = dexSection.offset + (Constants.TYPE_TYPE_ID_ITEM_SIZE * index);
int stringIndex = dexReader.getUint(offset);
return dexItemFactory.createType(indexedItems.getString(stringIndex));
}
private DexField fieldAt(int index) {
DexSection dexSection = lookupSection(Constants.TYPE_FIELD_ID_ITEM);
if (index >= dexSection.length) {
return null;
}
int offset = dexSection.offset + (Constants.TYPE_FIELD_ID_ITEM_SIZE * index);
dexReader.position(offset);
int classIndex = dexReader.getUshort();
int typeIndex = dexReader.getUshort();
int nameIndex = dexReader.getUint();
DexType clazz = indexedItems.getType(classIndex);
DexType type = indexedItems.getType(typeIndex);
DexString name = indexedItems.getString(nameIndex);
return dexItemFactory.createField(clazz, type, name);
}
private DexMethodHandle methodHandleAt(int index) {
DexSection dexSection = lookupSection(Constants.TYPE_METHOD_HANDLE_ITEM);
if (index >= dexSection.length) {
return null;
}
int offset = dexSection.offset + (Constants.TYPE_METHOD_HANDLE_ITEM_SIZE * index);
dexReader.position(offset);
MethodHandleType type = MethodHandleType.getKind(dexReader.getUshort());
dexReader.getUshort(); // unused
int indexFieldOrMethod = dexReader.getUshort();
DexMember<? extends DexItem, ? extends DexMember<?, ?>> fieldOrMethod;
switch (type) {
case INSTANCE_GET:
case INSTANCE_PUT:
case STATIC_GET:
case STATIC_PUT: {
fieldOrMethod = indexedItems.getField(indexFieldOrMethod);
break;
}
case INVOKE_CONSTRUCTOR:
case INVOKE_DIRECT:
case INVOKE_INTERFACE:
case INVOKE_INSTANCE:
case INVOKE_STATIC: {
fieldOrMethod = indexedItems.getMethod(indexFieldOrMethod);
break;
}
default:
throw new AssertionError("Method handle type unsupported in a dex file.");
}
dexReader.getUshort(); // unused
return dexItemFactory.createMethodHandle(
type, fieldOrMethod, type == MethodHandleType.INVOKE_INTERFACE);
}
private DexCallSite callSiteAt(int index) {
DexSection dexSection = lookupSection(Constants.TYPE_CALL_SITE_ID_ITEM);
if (index >= dexSection.length) {
return null;
}
int callSiteOffset =
dexReader.getUint(dexSection.offset + (Constants.TYPE_CALL_SITE_ID_ITEM_SIZE * index));
DexEncodedArray callSiteEncodedArray = encodedArrayAt(callSiteOffset);
DexValue[] values = callSiteEncodedArray.values;
assert values[0].isDexValueMethodHandle();
assert values[1].isDexValueString();
assert values[2].isDexValueMethodType();
return dexItemFactory.createCallSite(
values[1].asDexValueString().value,
values[2].asDexValueMethodType().value,
values[0].asDexValueMethodHandle().value,
// 3 means first extra args
Arrays.asList(Arrays.copyOfRange(values, 3, values.length)));
}
private DexProto protoAt(int index) {
DexSection dexSection = lookupSection(Constants.TYPE_PROTO_ID_ITEM);
if (index >= dexSection.length) {
return null;
}
int offset = dexSection.offset + (Constants.TYPE_PROTO_ID_ITEM_SIZE * index);
dexReader.position(offset);
int shortyIndex = dexReader.getUint();
int returnTypeIndex = dexReader.getUint();
int parametersOffsetIndex = dexReader.getUint();
DexString shorty = indexedItems.getString(shortyIndex);
DexType returnType = indexedItems.getType(returnTypeIndex);
DexTypeList parameters = typeListAt(parametersOffsetIndex);
return dexItemFactory.createProto(returnType, parameters, shorty);
}
private DexMethod methodAt(int index) {
DexSection dexSection = lookupSection(Constants.TYPE_METHOD_ID_ITEM);
if (index >= dexSection.length) {
return null;
}
int offset = dexSection.offset + (Constants.TYPE_METHOD_ID_ITEM_SIZE * index);
dexReader.position(offset);
int classIndex = dexReader.getUshort();
int protoIndex = dexReader.getUshort();
int nameIndex = dexReader.getUint();
return dexItemFactory.createMethod(
indexedItems.getType(classIndex),
indexedItems.getProto(protoIndex),
indexedItems.getString(nameIndex));
}
private static class AnnotationsDirectory {
private static final DexParameterAnnotation[] NO_PARAMETER_ANNOTATIONS =
new DexParameterAnnotation[0];
private static final DexFieldAnnotation[] NO_FIELD_ANNOTATIONS =
new DexFieldAnnotation[0];
private static final DexMethodAnnotation[] NO_METHOD_ANNOTATIONS =
new DexMethodAnnotation[0];
private static final AnnotationsDirectory THE_EMPTY_ANNOTATIONS_DIRECTORY =
new AnnotationsDirectory(DexAnnotationSet.empty(),
NO_FIELD_ANNOTATIONS, new DexMethodAnnotation[0],
NO_PARAMETER_ANNOTATIONS);
public final DexAnnotationSet clazz;
public final DexFieldAnnotation[] fields;
public final DexMethodAnnotation[] methods;
public final DexParameterAnnotation[] parameters;
AnnotationsDirectory(DexAnnotationSet clazz,
DexFieldAnnotation[] fields,
DexMethodAnnotation[] methods,
DexParameterAnnotation[] parameters) {
this.clazz = clazz == null ? DexAnnotationSet.empty() : clazz;
this.fields = fields == null ? NO_FIELD_ANNOTATIONS : fields;
this.methods = methods == null ? NO_METHOD_ANNOTATIONS : methods;
this.parameters = parameters == null ? NO_PARAMETER_ANNOTATIONS : parameters;
}
public static AnnotationsDirectory empty() {
return THE_EMPTY_ANNOTATIONS_DIRECTORY;
}
}
private static class AttributesAndAnnotations {
private final DexAnnotationSet originalAnnotations;
private EnclosingMethodAttribute enclosingMethodAttribute = null;
private List<InnerClassAttribute> innerClasses = null;
private List<DexAnnotation> lazyAnnotations = null;
private ClassSignature classSignature = ClassSignature.noSignature();
public DexAnnotationSet getAnnotations() {
if (lazyAnnotations != null) {
int size = lazyAnnotations.size();
return size == 0
? DexAnnotationSet.empty()
: new DexAnnotationSet(lazyAnnotations.toArray(DexAnnotation.EMPTY_ARRAY));
}
return originalAnnotations;
}
public List<InnerClassAttribute> getInnerClasses() {
return innerClasses == null ? Collections.emptyList() : innerClasses;
}
public EnclosingMethodAttribute getEnclosingMethodAttribute() {
return enclosingMethodAttribute;
}
public ClassSignature getClassSignature() {
return classSignature;
}
public AttributesAndAnnotations(
DexType type, Origin origin, DexAnnotationSet annotations, InternalOptions options) {
this.originalAnnotations = annotations;
DexType enclosingClass = null;
DexMethod enclosingMethod = null;
List<DexType> memberClasses = null;
DexItemFactory factory = options.dexItemFactory();
for (int i = 0; i < annotations.annotations.length; i++) {
DexAnnotation annotation = annotations.annotations[i];
if (DexAnnotation.isEnclosingClassAnnotation(annotation, factory)) {
ensureAnnotations(i);
enclosingClass = DexAnnotation.getEnclosingClassFromAnnotation(annotation, factory);
} else if (DexAnnotation.isEnclosingMethodAnnotation(annotation, factory)) {
ensureAnnotations(i);
enclosingMethod = DexAnnotation.getEnclosingMethodFromAnnotation(annotation, factory);
} else if (DexAnnotation.isInnerClassAnnotation(annotation, factory)) {
ensureAnnotations(i);
if (innerClasses == null) {
innerClasses = new ArrayList<>(annotations.annotations.length - i);
}
Pair<DexString, Integer> entry =
DexAnnotation.getInnerClassFromAnnotation(annotation, factory);
innerClasses.add(
new InnerClassAttribute(entry.getSecond(), type, null, entry.getFirst()));
} else if (DexAnnotation.isMemberClassesAnnotation(annotation, factory)) {
ensureAnnotations(i);
List<DexType> members = DexAnnotation.getMemberClassesFromAnnotation(annotation, factory);
if (memberClasses == null) {
memberClasses = members;
} else if (members != null) {
memberClasses.addAll(members);
}
} else if (DexAnnotation.isSignatureAnnotation(annotation, factory)
&& !options.passthroughDexCode) {
ensureAnnotations(i);
String signature = DexAnnotation.getSignature(annotation);
classSignature =
GenericSignature.parseClassSignature(
type.getName(), signature, origin, factory, options.reporter);
} else {
copyAnnotation(annotation);
}
}
if (enclosingClass != null || enclosingMethod != null) {
assert enclosingClass == null || enclosingMethod == null;
if (enclosingMethod != null) {
enclosingMethodAttribute = new EnclosingMethodAttribute(enclosingMethod);
} else {
InnerClassAttribute namedEnclosing = null;
if (innerClasses != null) {
for (InnerClassAttribute innerClass : innerClasses) {
if (type == innerClass.getInner()) {
// If inner-class is anonymous then we create an enclosing-method attribute.
// Unfortunately we can't distinguish member classes from local classes, and thus
// can't at this point conform to the spec which requires a enclosing-method
// attribute iff the inner-class is anonymous or local. A local inner class will
// thus be represented as an ordinary member class and given an inner-classes
// entry below.
namedEnclosing = innerClass.isNamed() ? innerClass : null;
break;
}
}
}
if (namedEnclosing == null) {
enclosingMethodAttribute = new EnclosingMethodAttribute(enclosingClass);
} else {
assert innerClasses != null;
innerClasses.remove(namedEnclosing);
innerClasses.add(
new InnerClassAttribute(
namedEnclosing.getAccess(),
type,
enclosingClass,
namedEnclosing.getInnerName()));
}
}
}
if (memberClasses != null) {
if (innerClasses == null) {
innerClasses = new ArrayList<>(memberClasses.size());
}
for (DexType memberClass : memberClasses) {
innerClasses.add(InnerClassAttribute.createUnknownNamedInnerClass(memberClass, type));
}
}
}
private void ensureAnnotations(int index) {
if (lazyAnnotations == null) {
lazyAnnotations = new ArrayList<>(originalAnnotations.annotations.length);
lazyAnnotations.addAll(Arrays.asList(originalAnnotations.annotations).subList(0, index));
}
}
private void copyAnnotation(DexAnnotation annotation) {
if (lazyAnnotations != null) {
lazyAnnotations.add(annotation);
}
}
}
}