Skip to content

Commit f3954c9

Browse files
authored
Add support for enum value comparison (#7418)
enum value is expressed as string with either fully qualified name, simple name with enum class of just the enum value.
1 parent 26db206 commit f3954c9

File tree

3 files changed

+101
-1
lines changed

3 files changed

+101
-1
lines changed

dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ComparisonOperator.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,52 @@ public Boolean apply(Value<?> left, Value<?> right) {
2121
}
2222
return compare(leftNumber, rightNumber) == 0;
2323
}
24+
if (left.getValue() instanceof Enum || right.getValue() instanceof Enum) {
25+
return applyEqualityForEnum(left, right);
26+
}
2427
if (left.getValue().getClass() == right.getValue().getClass()) {
2528
return Objects.equals(left.getValue(), right.getValue());
2629
}
2730
return Boolean.FALSE;
2831
}
32+
33+
private Boolean applyEqualityForEnum(Value<?> left, Value<?> right) {
34+
if (left.getValue() instanceof Enum && right instanceof StringValue) {
35+
return doApplyEqualityForEnum(left, right);
36+
}
37+
if (left instanceof StringValue && right.getValue() instanceof Enum) {
38+
return doApplyEqualityForEnum(right, left);
39+
}
40+
throw new EvaluationException(
41+
"Equality operator is not supported for the given types: "
42+
+ left.getValue().getClass().getName()
43+
+ " and "
44+
+ right.getValue().getClass().getName(),
45+
null);
46+
}
47+
48+
private Boolean doApplyEqualityForEnum(Value<?> enumExpr, Value<?> enumValueExpr) {
49+
Enum<?> enumValue = (Enum<?>) enumExpr.getValue();
50+
String enumValueStr = (String) enumValueExpr.getValue();
51+
Class<? extends Enum> enumClass = enumValue.getClass();
52+
Enum[] enumConstants = enumClass.getEnumConstants();
53+
for (Enum<?> enumConstant : enumConstants) {
54+
// Check if string constant as value expression matches for enum constant
55+
// the endsWith allow to match either:
56+
// - the full enum constant name (com.datadog.debugger.MyEnum.ONE)
57+
// - the simple name with enum class name (MyEnum.ONE)
58+
// - the simple name (ONE)
59+
// The second check against enumValue is to ensure the instance filtered based on the
60+
// name is still correct because the name can partially match (CLOSE in OPENCLOSE)
61+
// with an enum defined like (OPEN, CLOSE, OPENCLOSE)
62+
if (enumValueStr.endsWith(enumConstant.name())) {
63+
if (enumValue.equals(enumConstant)) {
64+
return Boolean.TRUE;
65+
}
66+
}
67+
}
68+
return Boolean.FALSE;
69+
}
2970
},
3071
GE(">=") {
3172
@Override

dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/ComparisonExpressionTest.java

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import com.datadog.debugger.el.values.StringValue;
1717
import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver;
1818
import java.math.BigDecimal;
19+
import java.nio.file.StandardOpenOption;
1920
import java.util.ArrayList;
2021
import java.util.Random;
2122
import java.util.stream.Stream;
@@ -164,7 +165,37 @@ private static Stream<Arguments> expressions() {
164165
new StringValue("java.lang.Double"),
165166
INSTANCEOF,
166167
true,
167-
"1.0 instanceof \"java.lang.Double\""));
168+
"1.0 instanceof \"java.lang.Double\""),
169+
Arguments.of(
170+
new ObjectValue(StandardOpenOption.READ),
171+
new StringValue("READ"),
172+
EQ,
173+
true,
174+
"java.nio.file.StandardOpenOption == \"READ\""),
175+
Arguments.of(
176+
new ObjectValue(StandardOpenOption.READ),
177+
new StringValue("StandardOpenOption.READ"),
178+
EQ,
179+
true,
180+
"java.nio.file.StandardOpenOption == \"StandardOpenOption.READ\""),
181+
Arguments.of(
182+
new ObjectValue(StandardOpenOption.READ),
183+
new StringValue("java.nio.file.StandardOpenOption.READ"),
184+
EQ,
185+
true,
186+
"java.nio.file.StandardOpenOption == \"java.nio.file.StandardOpenOption.READ\""),
187+
Arguments.of(
188+
new ObjectValue(StandardOpenOption.CREATE),
189+
new StringValue("READ"),
190+
EQ,
191+
false,
192+
"java.nio.file.StandardOpenOption == \"READ\""),
193+
Arguments.of(
194+
new StringValue("READ"),
195+
new ObjectValue(StandardOpenOption.READ),
196+
EQ,
197+
true,
198+
"\"READ\" == java.nio.file.StandardOpenOption"));
168199
}
169200

170201
@ParameterizedTest

dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1776,6 +1776,34 @@ public void enumValues() throws IOException, URISyntaxException {
17761776
"TWO");
17771777
}
17781778

1779+
@Test
1780+
public void enumCondition() throws IOException, URISyntaxException {
1781+
final String CLASS_NAME = "com.datadog.debugger.CapturedSnapshot23";
1782+
LogProbe probe =
1783+
createProbeBuilder(PROBE_ID, CLASS_NAME, "convert", null)
1784+
.evaluateAt(MethodLocation.EXIT)
1785+
.when(
1786+
new ProbeCondition(
1787+
DSL.when(
1788+
DSL.and(
1789+
DSL.eq(DSL.ref("@return"), DSL.value("TWO")),
1790+
DSL.eq(DSL.ref("@return"), DSL.value("MyEnum.TWO")),
1791+
DSL.eq(
1792+
DSL.ref("@return"),
1793+
DSL.value("com.datadog.debugger.CapturedSnapshot23$MyEnum.TWO")))),
1794+
"@return == 'TWO' && @return == 'MyEnum.TWO' && @return == 'com.datadog.debugger.CapturedSnapshot23$MyEnum.TWO'"))
1795+
.build();
1796+
TestSnapshotListener listener = installProbes(CLASS_NAME, probe);
1797+
Class<?> testClass = compileAndLoadClass(CLASS_NAME);
1798+
int result = Reflect.onClass(testClass).call("main", "2").get();
1799+
assertEquals(2, result);
1800+
Snapshot snapshot = assertOneSnapshot(listener);
1801+
assertCaptureReturnValue(
1802+
snapshot.getCaptures().getReturn(),
1803+
"com.datadog.debugger.CapturedSnapshot23$MyEnum",
1804+
"TWO");
1805+
}
1806+
17791807
@Test
17801808
public void recursiveCapture() throws IOException, URISyntaxException {
17811809
final String CLASS_NAME = "com.datadog.debugger.CapturedSnapshot24";

0 commit comments

Comments
 (0)