public @Nullable HeapItem getTerminatingRegisterConsensus(int register) { Map<Integer, HeapItem> items = getTerminatingRegisterConsensus(new int[]{ register }); return items.get(register); }
private static void executeParameterLogicWithUnknownParameter() throws VirtualMachineException { String methodSignature = "Lorg/cf/demosmali/Main;->parameterLogic(I)I"; // Execute with ambiguous / unknown parameters. This forces smalivm to take both execution paths. // You should see two prints, indicating it took both execution paths. ExecutionGraph graph = vm.execute(methodSignature); // Get the return value consensus over all possible execution paths. // These paths have different return values, so there's no consensus. HeapItem item = graph.getTerminatingRegisterConsensus(MethodState.ReturnRegister); System.out.println("With no context, returns an unknown integer: " + item); }
private static void testRegisterState(ExecutionGraph graph, Map<Integer, HeapItem> registerToItem) { for (Entry<Integer, HeapItem> entry : registerToItem.entrySet()) { Integer register = entry.getKey(); HeapItem expected = entry.getValue(); HeapItem actual = graph.getTerminatingRegisterConsensus(register); testRegisterEquals(expected, actual); } }
@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 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 canInvokeImplemenetedAbstractMethod() throws VirtualMachineException { String methodName = "abstractMethod()Ljava/lang/String;"; ExecutionGraph graph = vm.execute(CLASS_NAME + "->" + methodName); HeapItem item = graph.getTerminatingRegisterConsensus(MethodState.ReturnRegister); String value = (String) item.getValue(); assertEquals(EXPECTED_VALUE, value); }
@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 canInvokeAbstractMethodThroughParentReference() throws VirtualMachineException { String methodName = "callsAbstractMethod()Ljava/lang/String;"; ExecutionGraph graph = vm.execute(CLASS_NAME + "->" + methodName); HeapItem item = graph.getTerminatingRegisterConsensus(MethodState.ReturnRegister); assertEquals(EXPECTED_VALUE, item.getValue()); }
@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()); }
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); }
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 unsafeExceptionIsNotInstantiated() throws VirtualMachineException { String methodName = "createAndThrowException()V"; ExecutionGraph graph = vm.execute(CLASS_NAME, methodName); HeapItem item = graph.getTerminatingRegisterConsensus(0); assertEquals(EXCEPTION_CLASS_NAME, item.getType()); assertEquals(UninitializedInstance.class, item.getValue().getClass()); Instance instance = (Instance) item.getValue(); assertEquals(EXCEPTION_CLASS_NAME, instance.getType().getName()); }
@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()); }