public static ExecutionGraphManipulator getGraphManipulator(VirtualMachine vm, String className, String methodDescriptor, VMState initial) { ExecutionGraph graph = VMTester.execute(vm, className, methodDescriptor, initial); String methodSignature = className + "->" + methodDescriptor; VirtualMethod method = vm.getClassManager().getMethod(methodSignature); DexBuilder dexBuilder = VMTester.getDexBuilder(); return new ExecutionGraphManipulator(graph, method, vm, dexBuilder); }
public static ExecutionGraph execute(VirtualMachine vm, String className, String methodDescriptor, VMState state) { String methodSignature = className + "->" + methodDescriptor; ExecutionContext context = buildInitializedContext(vm, methodSignature, state); ExecutionGraph graph = null; try { graph = vm.execute(methodSignature, context); } catch (VirtualMachineException e) { e.printStackTrace(); } return graph; }
@Test public void longDivisionByZeroExceptionIsCaughtAndHasNoChildrenAndAssignsNoRegisters() { long value1 = 1120403456L; long value2 = 0L; VMTester.setRegisterMock(mState, ARG1_REGISTER, value1, "J"); VMTester.setRegisterMock(mState, ARG2_REGISTER, value2, "J"); buildInstruction23x(Opcode.DIV_LONG); op = opFactory.create(location, addressToLocation, vm); op.execute(node, mState); VMTester.verifyExceptionHandling(ArithmeticException.class, "/ by zero", node, mState); }
/** * Create a new {@link VirtualMachine} for testing. Since this is heavily used, it tries to avoid the main cost of creating a {@link * VirtualMachine} by reusing the same {@link ClassManager}. * * @return {@link VirtualMachine} for tests */ public static VirtualMachine spawnVM() { return spawnVM(false); }
@Test public void invokeNonExistentMethodThrowsException() { thrown.expect(RuntimeException.class); thrown.expectMessage("Can't find Smali file for Lim_not_your_friend_buddy;"); VMTester.execute(CLASS_NAME, "invokeNonExistentMethod()V"); }
@Test public void canConstClassLocal() throws ClassNotFoundException { VirtualMachine vm = VMTester.spawnVM(); Class<?> expectedClass = vm.getClassLoader().loadClass(ClassNameUtils.internalToBinary(CLASS_NAME)); expected.setRegisters(0, expectedClass, CommonTypes.CLASS); ExecutionGraph graph = VMTester.execute(vm, CLASS_NAME, "constClassLocal()V"); VMTester.testState(graph, expected); }
@Test public void canConstWide() { expected.setRegisters(0, 0x4242424242424242L, "D"); VMTester.test(CLASS_NAME, "constWide()V", expected); }
@Test public void sometimesThrownExceptionExecutesExceptionalAndNormalExecutionPaths() { initial.setRegisters(0, new UnknownValue(), "I"); ExecutionGraph graph = VMTester.execute(CLASS_NAME, "invokeMethodWhichMayThrowNullPointerException()V", initial); int[] expectedAddresses = new int[] { 0, 3, 4, 5 }; VMTester.testVisitation(graph, expectedAddresses); String exceptionClass = "Ljava/lang/NullPointerException;"; // Unknown type for register 0 because could be I or exceptionClass expected.setRegisters(MethodState.ThrowRegister, new UnknownValue(), exceptionClass, 0, new UnknownValue(), "?"); VMTester.testState(graph, expected); } }
@Test public void invokeVirtualManyParametersThrowsNoExceptions() throws InstantiationException, IllegalAccessException, ClassNotFoundException { VirtualMachine vm = VMTester.spawnVM(); Class<?> virtualClass = vm.getClassLoader().loadClass(CLASS_NAME_BINARY); Object instance = virtualClass.newInstance(); initial.setRegisters(0, instance, CLASS_NAME, 1, 0x100L, "J", 3, 0x200L, "J", 5, 0x300L, "J", 7, 0x3, "I"); expected.setRegisters(MethodState.ResultRegister, 0x3, "I"); VMTester.test(CLASS_NAME, "invokeRangeManyParameters()V", initial, expected); }
@Test public void testIfUnknownIntegerTakesBothPaths() { initial.setRegisters(0, new UnknownValue(), "I"); ExecutionGraph graph = VMTester.execute(CLASS_NAME, "ifEqualZero()V", initial); VMTester.testVisitation(graph, IF_ALL_VISITATIONS); assertEquals(1, graph.getNodePile(ADDRESS_NOP).size()); // Two sepearate execution paths should reach the return op assertEquals(2, graph.getNodePile(ADDRESS_RETURN).size()); }
@Test public void canCreateLocalInstance() throws ClassNotFoundException { initial.setRegisters(0, 1, "I"); ExecutionGraph graph = VMTester.execute(vm, CLASS_NAME, "newLocalInstance()V", initial); VirtualType instanceType = vm.getClassManager().getVirtualType(CLASS_NAME); expected.setRegisters(0, new UninitializedInstance(instanceType), CLASS_NAME); VMTester.testState(graph, expected); }
@Test public void canThrowNullPointerException() { ExecutionGraph graph = VMTester.execute(CLASS_NAME, "throwNullPointerException()V", initial); Class<?> exceptionClass = NullPointerException.class; HeapItem item = graph.getTerminatingRegisterConsensus(0); Assert.assertEquals(exceptionClass, item.getValue().getClass()); Assert.assertEquals(ClassNameUtils.toInternal(exceptionClass), item.getType()); HeapItem throwItem = graph.getTerminatingRegisterConsensus(MethodState.ThrowRegister); assertEquals(item, throwItem); VMTester.test(CLASS_NAME, "throwNullPointerException()V", expected); }
@Before public void setUp() { dexBuilder = VMTester.getDexBuilder(); }
public static void testState(ExecutionGraph graph, VMState expectedState) { assertNotNull("Graph is null. Failed to execute method.", graph); testRegisterState(graph, expectedState.getRegisters()); testClassState(graph, expectedState.getFields()); }
private static ExecutionContext buildInitializedContext(VirtualMachine vm, String methodSignature, VMState state) { VirtualMethod method = vm.getClassManager().getMethod(methodSignature); ExecutionContext context = vm.spawnRootContext(method); int registerCount = context.getMethodState().getRegisterCount(); setupMethodState(context, state.getRegisters(), registerCount); setupClassStates(context, vm, state.getFields()); return context; }
@Test public void doubleDivisionWithTwoRegistersEqualsExpected() { double value1 = 1586.2D; double value2 = 2536.9D; double expected = value1 / value2; VMTester.setRegisterMock(mState, ARG1_REGISTER, value1, "D"); VMTester.setRegisterMock(mState, ARG2_REGISTER, value2, "D"); buildInstruction12x(Opcode.DIV_DOUBLE); op = opFactory.create(location, addressToLocation, vm); op.execute(node, mState); verify(mState, times(1)).assignRegister(eq(ARG1_REGISTER), eq(expected), eq("D")); }
private static void testClassState(ExecutionGraph graph, Map<String, Map<String, HeapItem>> classNameToFieldDescriptorToItem) { for (Entry<String, Map<String, HeapItem>> fieldDescriptorMapEntry : classNameToFieldDescriptorToItem.entrySet()) { String className = fieldDescriptorMapEntry.getKey(); VirtualClass virtualClass = graph.getVM().getClassManager().getVirtualClass(className); Map<String, HeapItem> fieldDescriptorToItem = fieldDescriptorMapEntry.getValue(); for (Entry<String, HeapItem> entry : fieldDescriptorToItem.entrySet()) { String fieldDescriptor = entry.getKey(); String fieldName = fieldDescriptor.split(":")[0]; VirtualField field = virtualClass.getField(fieldName); HeapItem expected = entry.getValue(); HeapItem actual = graph.getTerminatingFieldConsensus(field); testFieldEquals(expected, actual); } } }
private static void spawnVM() { if (vm == null) { // Confession: no idea why but if we don't tell classmanager to perform an expensive recache of classes, // it doesn't think the dummy class exists. vm = VMTester.spawnVM(true); } }
@Test public void invokeReturnVoidReturnsVoid() { ExecutionGraph graph = VMTester.execute(CLASS_NAME, "invokeReturnVoid()V"); HeapItem consensus = graph.getTerminatingRegisterConsensus(MethodState.ResultRegister); assertNull("Consensus should be null", consensus); }