diff --git a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/util/WellKnownClasses.java b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/util/WellKnownClasses.java index c9b9ef5f50c..7512d1b0212 100644 --- a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/util/WellKnownClasses.java +++ b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/util/WellKnownClasses.java @@ -5,6 +5,7 @@ import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -14,6 +15,7 @@ import java.util.OptionalLong; import java.util.Set; import java.util.function.Function; +import java.util.function.ToLongFunction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -58,6 +60,13 @@ public class WellKnownClasses { // implementations of java.io.file.Path interfaces SAFE_TO_STRING_FUNCTIONS.put("sun.nio.fs.UnixPath", String::valueOf); SAFE_TO_STRING_FUNCTIONS.put("sun.nio.fs.WindowsPath", String::valueOf); + SAFE_TO_STRING_FUNCTIONS.put("java.util.Date", WellKnownClasses::dateToString); + } + + private static final Map> LONG_FUNCTIONS = new HashMap<>(); + + static { + LONG_FUNCTIONS.put("java.util.Date", WellKnownClasses::dateToLongValue); } private static final Set EQUALS_SAFE_CLASSES = new HashSet<>(); @@ -102,6 +111,8 @@ public class WellKnownClasses { "sun.nio.fs.UnixPath", "sun.nio.fs.WindowsPath")); + private static final Set LONG_PRIMITIVES = new HashSet<>(Arrays.asList("java.util.Date")); + private static final Map, Map>> SPECIAL_TYPE_ACCESS = new HashMap<>(); @@ -211,6 +222,14 @@ public static boolean isStringPrimitive(String type) { return STRING_PRIMITIVES.contains(type); } + /** + * indicates if type is considered as a int/long primitive and can be compared to another long + * value or literal with Expression Language + */ + public static boolean isLongPrimitive(String type) { + return LONG_PRIMITIVES.contains(type); + } + /** * @return a map of fields with function to access special field of a type, or null if type is not * supported. This is used to avoid using reflection to access fields on well known types @@ -232,6 +251,8 @@ public static Map> getSp } /** + * @param type the type name of the object to generate a string representation for. Must be a + * concrete type, not a declared type. see {@link #isToStringSafe(String)} * @return a function to generate a string representation of a type where the default toString * method is not suitable */ @@ -243,12 +264,24 @@ private static String classToString(Object o) { return ((Class) o).getTypeName(); } + private static String dateToString(Object o) { + return Long.toString(((Date) o).getTime()); + } + + private static long dateToLongValue(Object o) { + return ((Date) o).getTime(); + } + public static boolean isEqualsSafe(Class clazz) { return clazz.isPrimitive() || clazz.isEnum() || EQUALS_SAFE_CLASSES.contains(clazz.getTypeName()); } + public static ToLongFunction getLongPrimitiveValueFunction(String typeName) { + return LONG_FUNCTIONS.get(typeName); + } + private static class ThrowableFields { public static final String BECAUSE_OVERRIDDEN = "Special access method not safe to be called because overridden"; diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/Value.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/Value.java index 04f0d0362f3..64aa76c14fd 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/Value.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/Value.java @@ -15,6 +15,7 @@ import java.util.Map; import java.util.Set; import java.util.function.Function; +import java.util.function.ToLongFunction; /** Represents any value of the expression language */ public interface Value { @@ -64,6 +65,11 @@ static Value of(Object value) { } value = toString.apply(value); } + if (WellKnownClasses.isLongPrimitive(typeName)) { + ToLongFunction longPrimitiveValueFunction = + WellKnownClasses.getLongPrimitiveValueFunction(typeName); + value = longPrimitiveValueFunction.applyAsLong(value); + } if (value instanceof Boolean) { return new BooleanValue((Boolean) value); } else if (value instanceof Character) { diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/ProbeConditionTest.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/ProbeConditionTest.java index 7afef48bfbe..8d6f8a3eae3 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/ProbeConditionTest.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/ProbeConditionTest.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -199,12 +200,13 @@ void redaction() throws IOException { } @Test - void stringPrimitives() throws IOException { + void primitives() throws IOException { ProbeCondition probeCondition = loadFromResource("/test_conditional_10.json"); Map args = new HashMap<>(); args.put("uuid", UUID.fromString("a3cbe9e7-edd3-4bef-8e5b-59bfcb04cf91")); args.put("duration", Duration.ofSeconds(42)); args.put("clazz", "foo".getClass()); + args.put("now", new Date(1700000000000L)); // 2023-11-14T00:00:00Z ValueReferenceResolver ctx = RefResolverHelper.createResolver(args, null); assertTrue(probeCondition.execute(ctx)); } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/resources/test_conditional_10.json b/dd-java-agent/agent-debugger/debugger-el/src/test/resources/test_conditional_10.json index 2d0816bca47..e2f75d92ea8 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/resources/test_conditional_10.json +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/resources/test_conditional_10.json @@ -15,17 +15,12 @@ "eq": [ {"ref": "clazz"}, "java.lang.String"] - } + }, { + "eq": [ + {"ref": "now"}, + 1700000000000 + ] + } ] } } - - "dsl": "isEmpty(emptyStr) && isEmpty(emptyList) && isEmpty(emptyMap) && isEmpty(emptyArray) && isUndefined(@exception)", - "json": { - "and": [ - {"isEmpty": {"ref": "emptyStr"}}, - {"isEmpty": {"ref": "emptyList"}}, - {"isEmpty": {"ref": "emptyMap"}}, - {"isEmpty": {"ref": "emptyArray"}}, - {"isUndefined": {"ref": "@exception"}} - diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SnapshotSerializationTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SnapshotSerializationTest.java index 428dc98b792..843d6d00be2 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SnapshotSerializationTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SnapshotSerializationTest.java @@ -297,7 +297,7 @@ static class WellKnownClasses { UUID uuid = UUID.nameUUIDFromBytes("foobar".getBytes()); AtomicLong atomicLong = new AtomicLong(123); URI uri = URI.create("https://www.datadoghq.com"); - Optional maybeDate = Optional.of(new Date()); + Optional maybeDate = Optional.of(new Date(1700000000000L)); Optional empty = Optional.empty(); OptionalInt maybeInt = OptionalInt.of(42); OptionalDouble maybeDouble = OptionalDouble.of(3.14); @@ -344,13 +344,12 @@ public void wellKnownClasses() throws IOException { Map maybeDate = (Map) objLocalFields.get("maybeDate"); assertComplexClass(maybeDate, Optional.class.getTypeName()); Map maybeDateFields = (Map) maybeDate.get(FIELDS); - Map value = (Map) maybeDateFields.get("value"); - assertComplexClass(value, Date.class.getTypeName()); + assertPrimitiveValue(maybeDateFields, "value", Date.class.getTypeName(), "1700000000000"); // empty Map empty = (Map) objLocalFields.get("empty"); assertComplexClass(empty, Optional.class.getTypeName()); Map emptyFields = (Map) empty.get(FIELDS); - value = (Map) emptyFields.get("value"); + Map value = (Map) emptyFields.get("value"); assertEquals(Object.class.getTypeName(), value.get(TYPE)); assertTrue((Boolean) value.get(IS_NULL)); // maybeInt