private static void spawnExceptionChildren(ExecutionGraph graph, ExecutionNode node, ExceptionHandlerAddressResolver exceptionResolver) throws UnhandledVirtualException { if (node.mayThrowException()) { for (Throwable exception : node.getExceptions()) { if (log.isTraceEnabled()) { log.trace("{} may throw virtual exception: ", node, exception); } int childAddress = exceptionResolver.resolve(exception, node.getAddress()); if (childAddress >= 0) { ExecutionNode childNode = spawnChild(graph, node, childAddress); childNode.getContext().getMethodState().assignExceptionRegister(exception); } else { if (node.getChildLocations().length == 0) { if (log.isErrorEnabled()) { // No children, probably a real exception log.error("{} unhandled virtual exception: ", node, exception); } throw new UnhandledVirtualException(exception); } else { // Op has children, doesn't *always* throw, probably virtual exception if (log.isTraceEnabled()) { log.trace("{} possible unhandled virtual exception: ", node, exception); } } } } } }
@Test public void unknownValueItemMakesArrayUnknownAndDoesNotClearExceptions() { initial.setRegisters(0, new int[5], "[I", 1, 0, "I", 2, new UnknownValue(), "I"); ExecutionGraph graph = VMTester.execute(CLASS_NAME, "putWithCatch()V", initial); ExecutionNode putNode = graph.getNodePile(0).get(0); Set<Throwable> exceptions = putNode.getExceptions(); assertEquals(2, exceptions.size()); List<Class<?>> exceptionClasses = exceptions.stream().map(Throwable::getClass).collect(Collectors.toList()); assertTrue(exceptionClasses.contains(ArrayIndexOutOfBoundsException.class)); assertTrue(exceptionClasses.contains(NullPointerException.class)); HeapItem item = graph.getTerminatingRegisterConsensus(0); assertEquals(CommonTypes.OBJECT, item.getType()); assertEquals(UnknownValue.class, item.getValue().getClass()); }