diff --git a/javatools/src/main/java/org/xvm/runtime/template/annotations/xFuture.java b/javatools/src/main/java/org/xvm/runtime/template/annotations/xFuture.java index 74834026ea..a34b1dde74 100644 --- a/javatools/src/main/java/org/xvm/runtime/template/annotations/xFuture.java +++ b/javatools/src/main/java/org/xvm/runtime/template/annotations/xFuture.java @@ -859,17 +859,32 @@ protected int assign(Frame frame, int iReturn) { @Override public String toString() { - return "(" + m_clazz + ") " + ( - getFuture().isDone() ? "Completed: " + toSafeString(): "Not completed" - ); + return "(" + m_clazz + ") " + describe(getFuture()); } - protected String toSafeString() { - try { - return String.valueOf(getFuture().get()); - } catch (Throwable e) { - return Utils.translate(e).toString(); + /** + * Describe a future's state without blocking, allocating or throwing. Cancellation is + * tested before exceptional completion, because a cancelled future is also completed + * exceptionally. + * + * @param future the future to describe, or null - {@link FutureTupleHandle} has none when + * it holds no {@link FutureHandle} + * + * @return a description of the future's state + */ + private static String describe(CompletableFuture future) { + if (future == null) { + return ""; + } + if (!future.isDone()) { + return "Not completed"; + } + if (future.isCancelled()) { + return ""; } + return future.isCompletedExceptionally() + ? "" + : "Completed: " + future.getNow(null); } }