@Test public void invokeReturnVoidReturnsVoid() { ExecutionGraph graph = VMTester.execute(CLASS_NAME, "invokeReturnVoid()V"); HeapItem consensus = graph.getTerminatingRegisterConsensus(MethodState.ResultRegister); assertNull("Consensus should be null", consensus); }
@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 canCreate2DLocalInstanceArray() throws ClassNotFoundException { int length = 5; initial.setRegisters(0, length, "I"); ExecutionGraph graph = VMTester.execute(CLASS_NAME, "create2DLocalInstanceArray()V", initial); HeapItem consensus = graph.getTerminatingRegisterConsensus(0); assertEquals("[[" + CLASS_NAME, consensus.getType()); assertEquals(length, Array.getLength(consensus.getValue())); Class<?> actualClass = consensus.getValue().getClass(); assertEquals("[[" + CLASS_NAME, actualClass.getName()); }
@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 invokeGetClassOnSelfReturnsCorrectClass() 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); ExecutionGraph graph = VMTester.execute(vm, CLASS_NAME, "invokeGetClassOnThis()V", initial); HeapItem consensus = graph.getTerminatingRegisterConsensus(MethodState.ResultRegister); assertEquals(instance.getClass(), consensus.getValue()); } }
@Test public void getsExpectedConsensusTypeForTypesInSameHierarchyAndNull() { String methodDescriptor = "returnsObjectOrStringOrNull()Ljava/lang/Object;"; initial.setRegister(0, new UnknownValue(), "I"); ExecutionGraph graph = VMTester.execute(vm, CLASS_NAME, methodDescriptor, initial); HeapItem item = graph.getTerminatingRegisterConsensus(MethodState.ReturnRegister); assertEquals(CommonTypes.OBJECT, item.getType()); }
@Test public void getsMostRecentCommonAncestorForTypesNotInSameHierarchy() { String methodDescriptor = "storesStringOrInteger()V"; initial.setRegister(0, new UnknownValue(), "I"); ExecutionGraph graph = VMTester.execute(vm, CLASS_NAME, methodDescriptor, initial); HeapItem item = graph.getTerminatingRegisterConsensus(0); assertEquals(CommonTypes.OBJECT, item.getType()); }
@Test public void hasExpectedToString() { ExecutionGraph graph = VMTester.execute(CLASS_NAME, "simpleLoop()V"); ExecutionNode node = graph.getRoot(); MethodState state = node.getContext().getMethodState(); String expected = "params: 0, locals: 2\nv0: type=I, value=0"; assertEquals(expected, state.toString()); } }
private static void testException(String methodDescriptor, Class<?> exceptionClass, String exceptionMessage, VMState initial) { ExecutionGraph graph = VMTester.execute(CLASS_NAME, methodDescriptor, initial); HeapItem item = graph.getTerminatingRegisterConsensus(0); Assert.assertEquals(exceptionClass, item.getValue().getClass()); Assert.assertEquals(ClassNameUtils.toInternal(exceptionClass), item.getType()); Assert.assertEquals(exceptionMessage, ((Throwable) item.getValue()).getMessage()); assertFalse("Should not reach next instruction in non-exception execution path", graph.wasAddressReached(1)); MethodState mState = graph.getNodePile(0).get(0).getContext().getMethodState(); Assert.assertEquals(0, mState.getRegistersAssigned().length); }
@Test public void cloningNullWithArrayCloneThrowsException() { // Calling with Object clone (and not [Object clone) will always throw a NPE because // there's not enough type information to know it's an array clone. initial.setRegisters(0, null, "[Ljava/lang/Object;"); ExecutionGraph graph = VMTester.execute(CLASS_NAME, "invokeArrayClone()V", initial); HeapItem item = graph.getTerminatingRegisterConsensus(0); Class<?> exceptionClass = NullPointerException.class; Assert.assertEquals(exceptionClass, item.getValue().getClass()); Assert.assertEquals(ClassNameUtils.toInternal(exceptionClass), item.getType()); int[] expectedAddresses = new int[] { 0, 5, 6 }; VMTester.testVisitation(graph, expectedAddresses); }
private static void testException(String methodDescriptor, Class<?> exceptionClass, String exceptionMessage, VMState initial) { ExecutionGraph graph = VMTester.execute(CLASS_NAME, methodDescriptor, initial); HeapItem item = graph.getTerminatingRegisterConsensus(0); Assert.assertEquals(exceptionClass, item.getValue().getClass()); Assert.assertEquals(ClassNameUtils.toInternal(exceptionClass), item.getType()); Assert.assertEquals(exceptionMessage, ((Throwable) item.getValue()).getMessage()); assertFalse("Should not reach next instruction in non-exception execution path", graph.wasAddressReached(1)); MethodState mState = graph.getNodePile(0).get(0).getContext().getMethodState(); Assert.assertEquals(0, mState.getRegistersAssigned().length); }
private static void testException(String methodDescriptor, Class<?> exceptionClass, VMState initial) { ExecutionGraph graph = VMTester.execute(CLASS_NAME, methodDescriptor, initial); HeapItem item = graph.getTerminatingRegisterConsensus(0); Assert.assertEquals(exceptionClass, item.getValue().getClass()); Assert.assertEquals(ClassNameUtils.toInternal(exceptionClass), item.getType()); assertFalse("Should not reach next instruction in non-exception execution path", graph.wasAddressReached(2)); MethodState mState = graph.getNodePile(0).get(0).getContext().getMethodState(); Assert.assertEquals(0, mState.getRegistersAssigned().length); }
@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); }
private static void testException(String methodDescriptor, Class<?> exceptionClass) { ExecutionGraph graph = VMTester.execute(CLASS_NAME, methodDescriptor); HeapItem item = graph.getTerminatingRegisterConsensus(0); assertEquals(exceptionClass, item.getValue().getClass()); assertEquals(ClassNameUtils.toInternal(exceptionClass), item.getType()); assertFalse(graph.wasAddressReached(2)); MethodState mState = graph.getNodePile(0).get(0).getContext().getMethodState(); assertEquals(0, mState.getRegistersAssigned().length); }
@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 alwaysThrownExceptionIsBubbledUp() { ExecutionGraph graph = VMTester.execute(CLASS_NAME, "invokeMethodWhichThrowsNullPointerException()V"); HeapItem item = graph.getTerminatingRegisterConsensus(0); Class<?> exceptionClass = NullPointerException.class; Assert.assertEquals(exceptionClass, item.getValue().getClass()); Assert.assertEquals(ClassNameUtils.toInternal(exceptionClass), item.getType()); int[] expectedAddresses = new int[] { 0, 4, 5 }; VMTester.testVisitation(graph, expectedAddresses); HeapItem throwItem = graph.getTerminatingRegisterConsensus(MethodState.ThrowRegister); assertEquals(item, throwItem); }
@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); }
@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 enumClassInitializationCreatesCorrectClass() { VirtualMachine vm = VMTester.spawnVM(); VirtualType type = vm.getClassManager().getVirtualType("Lextends_enum;"); initial.setRegisters(0, new UninitializedInstance(type), "Lextends_enum;", 1, "NONE", "Ljava/lang/String;", 2, 0, "I", 3, 0, "I"); ExecutionGraph graph = VMTester.execute("Lextends_enum;", "<init>(Ljava/lang/String;II)V", initial); HeapItem consensus = graph.getTerminatingRegisterConsensus(0); assertEquals("Lextends_enum;", consensus.getType()); } }
@Test public void objectClassInitializationCreatesCorrectClass() { VirtualMachine vm = VMTester.spawnVM(); VirtualType type = vm.getClassManager().getVirtualType("Lhash_code;"); initial.setRegisters(0, new UninitializedInstance(type), "Lhash_code;"); ExecutionGraph graph = VMTester.execute("Lhash_code;", "<init>()V"); HeapItem consensus = graph.getTerminatingRegisterConsensus(0); assertEquals("Lhash_code;", consensus.getType()); }