private void recordDebugEventTargets(Int2ReferenceMap<Instruction> offsetToInstruction) { DexDebugInfo debugInfo = method.getCode().asDexCode().getDebugInfo(); if (debugInfo != null) { int address = 0; for (DexDebugEvent event : debugInfo.events) { if (event instanceof AdvancePC) { AdvancePC advance = (AdvancePC) event; address += advance.delta; Instruction target = offsetToInstruction.get(address); assert target != null; debugEventTargets.put(address, target); } else if (event instanceof Default) { Default defaultEvent = (Default) event; address += defaultEvent.getPCDelta(); Instruction target = offsetToInstruction.get(address); assert target != null; debugEventTargets.put(address, target); } } } }
private DebugLocalsChange createLocalsChange( Int2ReferenceMap<DebugLocalInfo> ending, Int2ReferenceMap<DebugLocalInfo> starting) { if (ending.isEmpty() && starting.isEmpty()) { return null; } if (ending.isEmpty() || starting.isEmpty()) { return new DebugLocalsChange(ending, starting); } IntSet unneeded = new IntArraySet(Math.min(ending.size(), starting.size())); for (Entry<DebugLocalInfo> entry : ending.int2ReferenceEntrySet()) { if (starting.get(entry.getIntKey()) == entry.getValue()) { unneeded.add(entry.getIntKey()); } } if (unneeded.size() == ending.size() && unneeded.size() == starting.size()) { return null; } IntIterator iterator = unneeded.iterator(); while (iterator.hasNext()) { int key = iterator.nextInt(); ending.remove(key); starting.remove(key); } return new DebugLocalsChange(ending, starting); }
public static boolean localsInfoMapsEqual( Int2ReferenceMap<DebugLocalInfo> set0, Int2ReferenceMap<DebugLocalInfo> set1) { if (set0 == null) { return set1 == null; } if (set1 == null) { return false; } if (set0.keySet().size() != set1.keySet().size()) { return false; } for (int i : set0.keySet()) { if (!set0.get(i).equals(set1.get(i))) { return false; } } return true; }
private void initialize(Int2ReferenceMap<DebugLocalInfo> locals) { assert arguments == null; assert emittedLocals == null; assert lastKnownLocals == null; assert startLine == NO_LINE_INFO; if (locals == null) { emittedLocals = Int2ReferenceMaps.emptyMap(); lastKnownLocals = Int2ReferenceMaps.emptyMap(); return; } // Implicitly open all unparameterized arguments. emittedLocals = new Int2ReferenceOpenHashMap<>(); for (Entry<DebugLocalInfo> entry : locals.int2ReferenceEntrySet()) { if (entry.getValue().signature == null) { emittedLocals.put(entry.getIntKey(), entry.getValue()); } } lastKnownLocals = new Int2ReferenceOpenHashMap<>(emittedLocals); }
@Test public void whenIndexDoesNotHaveAnyPreviousIntersectingListItShouldIndexListAsExpected() { final Int2ReferenceMap<IntList> expectedListsInvertedIndex = new Int2ReferenceOpenHashMap<IntList>() {{ put(1, new IntArrayList(new int[] {0})); put(3, new IntArrayList(new int[] {0})); put(4, new IntArrayList(new int[] {0})); put(5, new IntArrayList(new int[] {0})); put(8, new IntArrayList(new int[] {0})); }}; final InvertedListIntReferenceListsIndex listsIndex = new InvertedListIntReferenceListsIndex(); final int index = 0; final IntList list = new IntArrayList(new int[] {1, 3, 4, 5, 8}); listsIndex.addListIntoListsInvertedIndex(index, list); assertEquals( Helper.<Integer, Int2ReferenceMap<IntList>>translateToUtilsCollection(expectedListsInvertedIndex), Helper.<Integer, Int2ReferenceMap<IntList>>translateToUtilsCollection(listsIndex.listsInvertedIndex) ); }
@Test public void whenIndexHavePreviousIntersectingListItShouldIndexListAsExpected() { final Int2ReferenceMap<IntList> expectedListsInvertedIndex = new Int2ReferenceOpenHashMap<IntList>() {{ put(1, new IntArrayList(new int[] {1})); put(2, new IntArrayList(new int[] {0})); put(3, new IntArrayList(new int[] {0, 1})); put(4, new IntArrayList(new int[] {1})); put(5, new IntArrayList(new int[] {0, 1})); put(8, new IntArrayList(new int[] {1})); }}; final InvertedListIntReferenceListsIndex listsIndex = new InvertedListIntReferenceListsIndex(); listsIndex.listsInvertedIndex.put(2, new IntArrayList(new int[]{0})); listsIndex.listsInvertedIndex.put(3, new IntArrayList(new int[]{0})); listsIndex.listsInvertedIndex.put(5, new IntArrayList(new int[]{0})); final int index = 1; final IntList list = new IntArrayList(new int[] {1, 3, 4, 5, 8}); listsIndex.addListIntoListsInvertedIndex(index, list); assertEquals( Helper.<Integer, Int2ReferenceMap<IntList>>translateToUtilsCollection(expectedListsInvertedIndex), Helper.<Integer, Int2ReferenceMap<IntList>>translateToUtilsCollection(listsIndex.listsInvertedIndex) ); }
private void recordTryAndHandlerTargets( Int2ReferenceMap<Instruction> offsetToInstruction, Instruction lastInstruction) { DexCode code = method.getCode().asDexCode(); for (Try theTry : code.tries) { Instruction start = offsetToInstruction.get(theTry.startAddress); int endAddress = theTry.startAddress + theTry.instructionCount; TryTargets targets; if (endAddress > lastInstruction.getOffset()) { targets = new TryTargets(start, lastInstruction, true); } else { Instruction end = offsetToInstruction.get(endAddress); targets = new TryTargets(start, end, false); } assert theTry.startAddress == targets.getStartOffset(); assert theTry.instructionCount == targets.getStartToEndDelta(); tryTargets.put(theTry, targets); } if (code.handlers != null) { for (TryHandler handler : code.handlers) { List<Instruction> targets = new ArrayList<>(); if (handler.catchAllAddr != NO_HANDLER) { Instruction target = offsetToInstruction.get(handler.catchAllAddr); assert target != null; targets.add(target); } for (TypeAddrPair pair : handler.pairs) { Instruction target = offsetToInstruction.get(pair.addr); assert target != null; targets.add(target); } handlerTargets.put(handler, targets); } } }
public DebugLocalsChange( Int2ReferenceMap<DebugLocalInfo> ending, Int2ReferenceMap<DebugLocalInfo> starting) { super(null); assert !ending.isEmpty() || !starting.isEmpty(); this.ending = ending; this.starting = starting; }
private EnumSwitchInfo(DexType enumClass, Instruction ordinalInvoke, Instruction arrayGet, Instruction staticGet, Int2ReferenceMap<DexField> indexMap, Reference2IntMap ordinalsMap) { this.enumClass = enumClass; this.ordinalInvoke = ordinalInvoke; this.arrayGet = arrayGet; this.staticGet = staticGet; this.indexMap = indexMap; this.ordinalsMap = ordinalsMap; }
private static boolean localsEqual( Int2ReferenceMap<DebugLocalInfo> locals1, Int2ReferenceMap<DebugLocalInfo> locals2) { if (locals1 == locals2) { return true; } if (locals1.size() != locals2.size()) { return false; } for (Int2ReferenceMap.Entry<DebugLocalInfo> entry : locals1.int2ReferenceEntrySet()) { if (locals2.get(entry.getIntKey()) != entry.getValue()) { return false; } } return true; }
@Test public void whenIndexHavePreviousIndexedListsItShouldRemoveListFromIndexAsExpected() { final Int2ReferenceMap<IntList> expectedListsInvertedIndex = new Int2ReferenceOpenHashMap<IntList>() {{ put(2, new IntArrayList(new int[] {2})); put(3, new IntArrayList(new int[] {0})); put(5, new IntArrayList(new int[] {0, 2})); put(9, new IntArrayList(new int[] {2})); }}; final InvertedListIntReferenceListsIndex listsIndex = new InvertedListIntReferenceListsIndex() {{ listsInvertedIndex.put(1, new IntArrayList(new int[] {1})); listsInvertedIndex.put(2, new IntArrayList(new int[] {2})); listsInvertedIndex.put(3, new IntArrayList(new int[] {0, 1})); listsInvertedIndex.put(4, new IntArrayList(new int[] {1})); listsInvertedIndex.put(5, new IntArrayList(new int[] {0, 1, 2})); listsInvertedIndex.put(8, new IntArrayList(new int[] {1})); listsInvertedIndex.put(9, new IntArrayList() {{ add(2); }}); }}; final int index = 1; final IntList list = new IntArrayList(new int[] {1, 3, 4, 5, 8}); listsIndex.removeListFromListsInvertedIndex(index, list); assertEquals( Helper.<Integer, Int2ReferenceMap<IntList>>translateToUtilsCollection(expectedListsInvertedIndex), Helper.<Integer, Int2ReferenceMap<IntList>>translateToUtilsCollection(listsIndex.listsInvertedIndex) ); }
private void recordInstructionTargets(Int2ReferenceMap<Instruction> offsetToInstruction) { Instruction[] instructions = method.getCode().asDexCode().instructions; for (Instruction instruction : instructions) { if (instruction instanceof Format22t) { // IfEq, IfGe, IfGt, IfLe, IfLt, IfNe Format22t condition = (Format22t) instruction; Instruction target = offsetToInstruction.get(condition.getOffset() + condition.CCCC); assert target != null; instructionTargets.put(instruction, Lists.newArrayList(target)); } else if (instruction instanceof Format21t) { // IfEqz, IfGez, IfGtz, IfLez, IfLtz, IfNez Format21t condition = (Format21t) instruction; Instruction target = offsetToInstruction.get(condition.getOffset() + condition.BBBB); assert target != null; instructionTargets.put(instruction, Lists.newArrayList(target)); } else if (instruction instanceof Goto) { Goto jump = (Goto) instruction; Instruction target = offsetToInstruction.get(jump.getOffset() + jump.AA); assert target != null; instructionTargets.put(instruction, Lists.newArrayList(target)); } else if (instruction instanceof Goto16) { Goto16 jump = (Goto16) instruction; Instruction target = offsetToInstruction.get(jump.getOffset() + jump.AAAA); assert target != null; instructionTargets.put(instruction, Lists.newArrayList(target)); } else if (instruction instanceof Goto32) { Goto32 jump = (Goto32) instruction; Instruction target = offsetToInstruction.get(jump.getOffset() + jump.AAAAAAAA); assert target != null; instructionTargets.put(instruction, Lists.newArrayList(target)); } else if (instruction instanceof Format31t) { // FillArrayData, SparseSwitch, PackedSwitch Format31t offsetInstruction = (Format31t) instruction; Instruction target = offsetToInstruction.get( offsetInstruction.getOffset() + offsetInstruction.getPayloadOffset()); assert target != null; instructionTargets.put(instruction, Lists.newArrayList(target)); } else if (instruction instanceof SwitchPayload) { SwitchPayload payload = (SwitchPayload) instruction; int[] targetOffsets = payload.switchTargetOffsets(); int switchOffset = payloadToSwitch.get(instruction).getOffset(); List<Instruction> targets = new ArrayList<>(); for (int i = 0; i < targetOffsets.length; i++) { Instruction target = offsetToInstruction.get(switchOffset + targetOffsets[i]); assert target != null; targets.add(target); } instructionTargets.put(instruction, targets); } } }
private void fixupLocalsLiveAtMoveException( BasicBlock block, ListIterator<Instruction> instructionIterator, List<LocalRange> openRanges, Int2ReferenceMap<DebugLocalInfo> finalLocals) { Int2ReferenceMap<DebugLocalInfo> initialLocals = new Int2ReferenceOpenHashMap<>(); int exceptionalIndex = block.getPredecessors().get(0).exceptionalExit().getNumber(); for (LocalRange open : openRanges) { int exceptionalRegister = getArgumentOrAllocateRegisterForValue(open.value, exceptionalIndex); initialLocals.put(exceptionalRegister, open.local); } block.setLocalsAtEntry(new Int2ReferenceOpenHashMap<>(initialLocals)); Instruction entry = instructionIterator.next(); assert block.entry() == entry; assert block.entry().isMoveException(); // Moves may have clobber current locals so they must be closed. Int2ReferenceMap<DebugLocalInfo> clobbered = endClobberedLocals(instructionIterator, initialLocals, finalLocals); for (Entry<DebugLocalInfo> ended : clobbered.int2ReferenceEntrySet()) { assert initialLocals.get(ended.getIntKey()) == ended.getValue(); initialLocals.remove(ended.getIntKey()); } instructionIterator.previous(); // Compute the final change in locals and insert it after the last move. Int2ReferenceMap<DebugLocalInfo> ending = new Int2ReferenceOpenHashMap<>(); Int2ReferenceMap<DebugLocalInfo> starting = new Int2ReferenceOpenHashMap<>(); for (Entry<DebugLocalInfo> initialLocal : initialLocals.int2ReferenceEntrySet()) { if (finalLocals.get(initialLocal.getIntKey()) != initialLocal.getValue()) { ending.put(initialLocal.getIntKey(), initialLocal.getValue()); } } for (Entry<DebugLocalInfo> finalLocal : finalLocals.int2ReferenceEntrySet()) { if (initialLocals.get(finalLocal.getIntKey()) != finalLocal.getValue()) { starting.put(finalLocal.getIntKey(), finalLocal.getValue()); } } DebugLocalsChange change = createLocalsChange(ending, starting); if (change != null) { instructionIterator.add(change); } }
public Int2ReferenceMap<DebugLocalInfo> getEnding() { return ending; }
public Int2ReferenceMap<DebugLocalInfo> getStarting() { return starting; }
public void setLocalsAtEntry(Int2ReferenceMap<DebugLocalInfo> localsAtEntry) { this.localsAtEntry = localsAtEntry; }
public Int2ReferenceMap<DebugLocalInfo> getLocalsAtEntry() { return localsAtEntry; }
/** * Looks for a switch statement over the enum companion class of the form * * <blockquote><pre> * switch(CompanionClass.$switchmap$field[enumValue.ordinal()]) { * ... * } * </pre></blockquote> * * and extracts the components and the index and ordinal maps. See * {@link EnumOrdinalMapCollector} and * {@link SwitchMapCollector} for details. */ public static EnumSwitchInfo analyzeSwitchOverEnum(Instruction switchInsn, AppInfoWithLiveness appInfo) { Instruction input = switchInsn.inValues().get(0).definition; if (input == null || !input.isArrayGet()) { return null; } ArrayGet arrayGet = input.asArrayGet(); Instruction index = arrayGet.index().definition; if (index == null || !index.isInvokeVirtual()) { return null; } InvokeVirtual ordinalInvoke = index.asInvokeVirtual(); DexMethod ordinalMethod = ordinalInvoke.getInvokedMethod(); DexClass enumClass = appInfo.definitionFor(ordinalMethod.holder); DexItemFactory dexItemFactory = appInfo.dexItemFactory; // After member rebinding, enumClass will be the actual java.lang.Enum class. if (enumClass == null || (!enumClass.accessFlags.isEnum() && enumClass.type != dexItemFactory.enumType) || ordinalMethod.name != dexItemFactory.ordinalMethodName || ordinalMethod.proto.returnType != dexItemFactory.intType || !ordinalMethod.proto.parameters.isEmpty()) { return null; } Instruction array = arrayGet.array().definition; if (array == null || !array.isStaticGet()) { return null; } StaticGet staticGet = array.asStaticGet(); Int2ReferenceMap<DexField> indexMap = SwitchMapCollector.getSwitchMapFor(staticGet.getField(), appInfo); if (indexMap == null || indexMap.isEmpty()) { return null; } // Due to member rebinding, only the fields are certain to provide the actual enums // class. DexType enumTyoe = indexMap.values().iterator().next().getHolder(); Reference2IntMap ordinalsMap = EnumOrdinalMapCollector.getOrdinalsMapFor(enumTyoe, appInfo); if (ordinalsMap == null) { return null; } return new EnumSwitchInfo(enumTyoe, ordinalInvoke, arrayGet, staticGet, indexMap, ordinalsMap); }
public static Int2ReferenceMap<DexField> getSwitchMapFor(DexField field, AppInfoWithLiveness appInfo) { Map<DexField, Int2ReferenceMap<DexField>> switchMaps = appInfo .getExtension(SwitchMapCollector.class, Collections.emptyMap()); return switchMaps.get(field); }
private void extractSwitchMap(DexEncodedField encodedField, IRCode initializer) { DexField field = encodedField.field; Int2ReferenceMap<DexField> switchMap = new Int2ReferenceArrayMap<>(); InstructionIterator it = initializer.instructionIterator(); Instruction insn; Predicate<Instruction> predicate = i -> i.isStaticGet() && i.asStaticGet().getField() == field; while ((insn = it.nextUntil(predicate)) != null) { for (Instruction use : insn.outValue().uniqueUsers()) { if (use.isArrayPut()) { Instruction index = use.asArrayPut().source().definition; if (index == null || !index.isConstNumber()) { return; } int integerIndex = index.asConstNumber().getIntValue(); Instruction value = use.asArrayPut().index().definition; if (value == null || !value.isInvokeVirtual()) { return; } InvokeVirtual invoke = value.asInvokeVirtual(); DexClass holder = appInfo.definitionFor(invoke.getInvokedMethod().holder); if (holder == null || (!holder.accessFlags.isEnum() && holder.type != appInfo.dexItemFactory.enumType)) { return; } Instruction enumGet = invoke.arguments().get(0).definition; if (enumGet == null || !enumGet.isStaticGet()) { return; } DexField enumField = enumGet.asStaticGet().getField(); if (!appInfo.definitionFor(enumField.getHolder()).accessFlags.isEnum()) { return; } if (switchMap.put(integerIndex, enumField) != null) { return; } } else { return; } } } switchMaps.put(field, switchMap); }
public static void emitLocalChangeEvents( Int2ReferenceMap<DebugLocalInfo> previousLocals, Int2ReferenceMap<DebugLocalInfo> nextLocals, Int2ReferenceMap<DebugLocalInfo> lastKnownLocals, List<DexDebugEvent> events, DexItemFactory factory) { Int2ReferenceSortedMap<DebugLocalInfo> ending = new Int2ReferenceAVLTreeMap<>(); Int2ReferenceSortedMap<DebugLocalInfo> starting = new Int2ReferenceAVLTreeMap<>(); for (Entry<DebugLocalInfo> entry : previousLocals.int2ReferenceEntrySet()) { int register = entry.getIntKey(); DebugLocalInfo local = entry.getValue(); if (nextLocals.get(register) != local) { ending.put(register, local); } } for (Entry<DebugLocalInfo> entry : nextLocals.int2ReferenceEntrySet()) { int register = entry.getIntKey(); DebugLocalInfo local = entry.getValue(); if (previousLocals.get(register) != local) { starting.put(register, local); } } assert !ending.isEmpty() || !starting.isEmpty(); for (Entry<DebugLocalInfo> end : ending.int2ReferenceEntrySet()) { int register = end.getIntKey(); if (!starting.containsKey(register)) { previousLocals.remove(register); events.add(factory.createEndLocal(register)); } } for (Entry<DebugLocalInfo> start : starting.int2ReferenceEntrySet()) { int register = start.getIntKey(); DebugLocalInfo local = start.getValue(); previousLocals.put(register, local); if (lastKnownLocals.get(register) == local) { events.add(factory.createRestartLocal(register)); } else { events.add(new StartLocal(register, local)); lastKnownLocals.put(register, local); } } }
public Int2ReferenceMap<DebugLocalInfo> getArguments() { return arguments; }