11package datadog .trace .bootstrap .debugger .util ;
22
3+ import static datadog .trace .api .telemetry .LogCollector .EXCLUDE_TELEMETRY ;
4+ import static java .lang .invoke .MethodType .methodType ;
5+
6+ import datadog .environment .JavaVirtualMachine ;
37import datadog .trace .bootstrap .debugger .CapturedContext ;
8+ import java .lang .invoke .MethodHandle ;
9+ import java .lang .invoke .MethodHandles ;
410import java .lang .reflect .InvocationTargetException ;
511import java .lang .reflect .Method ;
612import java .util .Arrays ;
1420import java .util .OptionalInt ;
1521import java .util .OptionalLong ;
1622import java .util .Set ;
23+ import java .util .concurrent .CompletableFuture ;
1724import java .util .function .Function ;
1825import java .util .function .ToLongFunction ;
1926import org .slf4j .Logger ;
@@ -144,12 +151,18 @@ public class WellKnownClasses {
144151 OPTIONALDOUBLE_SPECIAL_FIELDS = new HashMap <>();
145152 private static final Map <String , Function <Object , CapturedContext .CapturedValue >>
146153 OPTIONALLONG_SPECIAL_FIELDS = new HashMap <>();
154+ private static final Map <String , Function <Object , CapturedContext .CapturedValue >>
155+ COMPLETABLEFUTURE_SPECIAL_FIELDS = new HashMap <>();
147156
148157 static {
149158 OPTIONAL_SPECIAL_FIELDS .put ("value" , OptionalFields ::value );
150159 OPTIONALINT_SPECIAL_FIELDS .put ("value" , OptionalFields ::valueInt );
151160 OPTIONALDOUBLE_SPECIAL_FIELDS .put ("value" , OptionalFields ::valueDouble );
152161 OPTIONALLONG_SPECIAL_FIELDS .put ("value" , OptionalFields ::valueLong );
162+ if (JavaVirtualMachine .isJavaVersionAtLeast (19 )) {
163+ // Future::resultNow method is available since JDK 19
164+ COMPLETABLEFUTURE_SPECIAL_FIELDS .put ("result" , CompletableFutureFields ::result );
165+ }
153166 }
154167
155168 static {
@@ -158,6 +171,10 @@ public class WellKnownClasses {
158171 SPECIAL_TYPE_ACCESS .put (OptionalInt .class , OPTIONALINT_SPECIAL_FIELDS );
159172 SPECIAL_TYPE_ACCESS .put (OptionalDouble .class , OPTIONALDOUBLE_SPECIAL_FIELDS );
160173 SPECIAL_TYPE_ACCESS .put (OptionalLong .class , OPTIONALLONG_SPECIAL_FIELDS );
174+ if (JavaVirtualMachine .isJavaVersionAtLeast (19 )) {
175+ // Future::resultNow method is available since JDK 19
176+ SPECIAL_TYPE_ACCESS .put (CompletableFuture .class , COMPLETABLEFUTURE_SPECIAL_FIELDS );
177+ }
161178 }
162179
163180 private static final Map <String , Function <Object , CapturedContext .CapturedValue >>
@@ -405,4 +422,36 @@ public static CapturedContext.CapturedValue valueLong(Object o) {
405422 "value" , Long .TYPE .getTypeName (), ((OptionalLong ) o ).orElse (0L ));
406423 }
407424 }
425+
426+ private static class CompletableFutureFields {
427+ private static final MethodHandle RESULT_NOW ;
428+
429+ static {
430+ MethodHandle methodHandle = null ;
431+ try {
432+ MethodHandles .Lookup lookup = MethodHandles .lookup ();
433+ methodHandle =
434+ lookup .findVirtual (CompletableFuture .class , "resultNow" , methodType (Object .class ));
435+ } catch (Exception e ) {
436+ LOGGER .debug (EXCLUDE_TELEMETRY , "Looking up CompletableFuture::resultNow failed: " , e );
437+ }
438+ RESULT_NOW = methodHandle ;
439+ }
440+
441+ public static CapturedContext .CapturedValue result (Object o ) {
442+ if (RESULT_NOW == null ) {
443+ throw new UnsupportedOperationException ("CompletableFuture::resultNow not available" );
444+ }
445+ try {
446+ CompletableFuture <?> future = (CompletableFuture <?>) o ;
447+ // need to check with isDone() to avoid getting exception if null.
448+ // Known benign rare race condition result != null => result == null
449+ // between isDone() and resultNow()
450+ Object result = future .isDone () ? RESULT_NOW .invokeExact ((future )) : null ;
451+ return CapturedContext .CapturedValue .of ("result" , Object .class .getTypeName (), result );
452+ } catch (Throwable t ) {
453+ throw new RuntimeException (t );
454+ }
455+ }
456+ }
408457}
0 commit comments