@Nonnull @Override public VariableSizeIterator<String> getParameterNames(@Nullable DexReader reader) { if (reader == null) { reader = dexFile.readerAt(debugInfoOffset); reader.skipUleb128(); } // TODO: make sure dalvik doesn't allow more parameter names than we have parameters final int parameterNameCount = reader.readSmallUleb128(); return new VariableSizeIterator<String>(reader, parameterNameCount) { @Override protected String readNextItem(@Nonnull DexReader reader, int index) { return dexFile.getOptionalString(reader.readSmallUleb128() - 1); } }; }
@Nonnull public static String asString(@Nonnull DexBackedDexFile dexFile, int methodIndex) { int methodOffset = dexFile.getMethodIdItemOffset(methodIndex); int classIndex = dexFile.readUshort(methodOffset + CLASS_OFFSET); String classType = dexFile.getType(classIndex); int protoIndex = dexFile.readUshort(methodOffset + PROTO_OFFSET); String protoString = ProtoIdItem.asString(dexFile, protoIndex); int nameIndex = dexFile.readSmallUint(methodOffset + NAME_OFFSET); String methodName = dexFile.getString(nameIndex); return String.format("%s->%s%s", classType, methodName, protoString); }
@Nonnull public static List<Set<? extends DexBackedAnnotation>> getParameterAnnotations( @Nonnull final DexBackedDexFile dexFile, final int annotationSetListOffset) { if (annotationSetListOffset > 0) { final int size = dexFile.readSmallUint(annotationSetListOffset); return new FixedSizeList<Set<? extends DexBackedAnnotation>>() { @Nonnull @Override public Set<? extends DexBackedAnnotation> readItem(int index) { int annotationSetOffset = dexFile.readSmallUint(annotationSetListOffset + 4 + index * 4); return getAnnotations(dexFile, annotationSetOffset); } @Override public int size() { return size; } }; } return ImmutableList.of(); }
@Nonnull public static Set<? extends DexBackedAnnotation> getAnnotations( @Nonnull final DexBackedDexFile dexFile, final int annotationSetOffset) { if (annotationSetOffset != 0) { final int size = dexFile.readSmallUint(annotationSetOffset); return new FixedSizeSet<DexBackedAnnotation>() { @Nonnull @Override public DexBackedAnnotation readItem(int index) { int annotationOffset = dexFile.readSmallUint(annotationSetOffset + 4 + (4 * index)); return new DexBackedAnnotation(dexFile, annotationOffset); } @Override public int size() { return size; } }; } return ImmutableSet.of(); }
@Nonnull @Override public Iterator<DebugItem> iterator() { DexReader reader = dexFile.readerAt(debugInfoOffset); final int lineNumberStart = reader.readBigUleb128(); int registerCount = methodImpl.getRegisterCount(); // TODO: does dalvik allow references to invalid registers? final LocalInfo[] locals = new LocalInfo[registerCount]; Arrays.fill(locals, EMPTY_LOCAL_INFO); DexBackedMethod method = methodImpl.method; // Create a MethodParameter iterator that uses our DexReader instance to read the parameter // names. // After we have finished iterating over the parameters, reader will "point to" the beginning // of the // debug instructions final Iterator<? extends MethodParameter> parameterIterator = new ParameterIterator( method.getParameterTypes(), method.getParameterAnnotations(), getParameterNames(reader)); // first, we grab all the parameters and temporarily store them at the beginning of locals, // disregarding any wide types int parameterIndex = 0; if (!AccessFlags.STATIC.isSet(methodImpl.method.getAccessFlags())) { // add the local info for the "this" parameter locals[parameterIndex++] = new LocalInfo() { @Override public String getName() { return "this"; } @Override public String getType() { return methodImpl.method.getDefiningClass(); } @Override public String getSignature() { return null; } }; } while (parameterIterator.hasNext()) { locals[parameterIndex++] = parameterIterator.next(); } if (parameterIndex < registerCount) { // now, we push the parameter locals back to their appropriate register, starting from the // end int localIndex = registerCount - 1; while (--parameterIndex > -1) { LocalInfo currentLocal = locals[parameterIndex]; String type = currentLocal.getType(); if (type != null && (type.equals("J") || type.equals("D"))) { localIndex--; if (localIndex == parameterIndex) { // there's no more room to push, the remaining registers are already in the correct // place break; } } locals[localIndex] = currentLocal; locals[parameterIndex] = EMPTY_LOCAL_INFO; localIndex--; } } return new VariableSizeLookaheadIterator<DebugItem>(dexFile, reader.getOffset()) { private int codeAddress = 0; private int lineNumber = lineNumberStart; @Nullable protected DebugItem readNextItem(@Nonnull DexReader reader) { while (true) { int next = reader.readUbyte(); switch (next) { case DebugItemType.END_SEQUENCE: { return null; } case DebugItemType.ADVANCE_PC: { int addressDiff = reader.readSmallUleb128(); codeAddress += addressDiff; continue; } case DebugItemType.ADVANCE_LINE: { int lineDiff = reader.readSleb128(); lineNumber += lineDiff; continue; } case DebugItemType.START_LOCAL: { int register = reader.readSmallUleb128(); String name = dexFile.getOptionalString(reader.readSmallUleb128() - 1); String type = dexFile.getOptionalType(reader.readSmallUleb128() - 1); ImmutableStartLocal startLocal = new ImmutableStartLocal(codeAddress, register, name, type, null); locals[register] = startLocal; return startLocal; } case DebugItemType.START_LOCAL_EXTENDED: { int register = reader.readSmallUleb128(); String name = dexFile.getOptionalString(reader.readSmallUleb128() - 1); String type = dexFile.getOptionalType(reader.readSmallUleb128() - 1); String signature = dexFile.getOptionalString(reader.readSmallUleb128() - 1); ImmutableStartLocal startLocal = new ImmutableStartLocal(codeAddress, register, name, type, signature); locals[register] = startLocal; return startLocal; } case DebugItemType.END_LOCAL: { int register = reader.readSmallUleb128(); LocalInfo localInfo = locals[register]; boolean replaceLocalInTable = true; if (localInfo instanceof EndLocal) { localInfo = EMPTY_LOCAL_INFO; // don't replace the local info in locals. The new EndLocal won't have any info // at all, // and we dont want to wipe out what's there, so that it is available for a // subsequent // RestartLocal replaceLocalInTable = false; } ImmutableEndLocal endLocal = new ImmutableEndLocal( codeAddress, register, localInfo.getName(), localInfo.getType(), localInfo.getSignature()); if (replaceLocalInTable) { locals[register] = endLocal; } return endLocal; } case DebugItemType.RESTART_LOCAL: { int register = reader.readSmallUleb128(); LocalInfo localInfo = locals[register]; ImmutableRestartLocal restartLocal = new ImmutableRestartLocal( codeAddress, register, localInfo.getName(), localInfo.getType(), localInfo.getSignature()); locals[register] = restartLocal; return restartLocal; } case DebugItemType.PROLOGUE_END: { return new ImmutablePrologueEnd(codeAddress); } case DebugItemType.EPILOGUE_BEGIN: { return new ImmutableEpilogueBegin(codeAddress); } case DebugItemType.SET_SOURCE_FILE: { String sourceFile = dexFile.getOptionalString(reader.readSmallUleb128() - 1); return new ImmutableSetSourceFile(codeAddress, sourceFile); } default: { int adjusted = next - 0x0A; codeAddress += adjusted / 15; lineNumber += (adjusted % 15) - 4; return new ImmutableLineNumber(codeAddress, lineNumber); } } } } }; }
public DexBackedSparseSwitchPayload(@Nonnull DexBackedDexFile dexFile, int instructionStart) { super(dexFile, Opcode.SPARSE_SWITCH_PAYLOAD, instructionStart); elementCount = dexFile.readUshort(instructionStart + ELEMENT_COUNT_OFFSET); }
@Nonnull public Set<? extends DexBackedAnnotation> getClassAnnotations() { return getAnnotations(dexFile, dexFile.readSmallUint(directoryOffset)); }
public int getParameterAnnotationCount() { return dexFile.readSmallUint(directoryOffset + PARAMETER_COUNT_OFFSET); }
public int getMethodAnnotationCount() { return dexFile.readSmallUint(directoryOffset + METHOD_COUNT_OFFSET); }
public int getFieldAnnotationCount() { return dexFile.readSmallUint(directoryOffset + FIELD_COUNT_OFFSET); }